<!DOCTYPE html><html><head><meta charset="utf-8"><title>Angular - HTTP</title><base href="/"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="search" type="application/opensearchdescription+xml" href="assets/opensearch.xml"><link rel="icon" type="image/x-icon" href="assets/images/favicons/favicon.ico"><link rel="icon" type="image/png" href="assets/images/favicons/favicon-32x32.png" sizes="32x32"><link rel="icon" type="image/png" href="assets/images/favicons/favicon-194x194.png" sizes="194x194"><link rel="icon" type="image/png" href="assets/images/favicons/favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/png" href="assets/images/favicons/favicon-16x16.png" sizes="16x16"><link rel="apple-touch-icon" sizes="144x144" href="assets/images/favicons/favicon-144x144.png"><link rel="apple-touch-icon-precomposed" sizes="144x144" href="assets/images/favicons/favicon-144x144.png"><link href="assets/fonts/Material_Icons.css" rel="stylesheet"><link href="assets/fonts/Droid_Sans_Mono.css" rel="stylesheet"><link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"><link rel="manifest" href="pwa-manifest.json"><meta name="theme-color" content="#1976d2"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="translucent"><script async="" src="assets/js/analytics.js"></script><script>!function(e,a,n,t,s,c,g){e.GoogleAnalyticsObject=s,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,c=a.createElement(n),g=a.getElementsByTagName(n)[0],c.async=1,c.src="assets/js/analytics.js",~e.name.indexOf("NG_DEFER_BOOTSTRAP")||g.parentNode.insertBefore(c,g)}(window,document,"script",0,"ga")</script><script>window.onerror=function(){ga("send","exception",{exDescription:function(e,r,n,a,c){var l;e=e.replace(/^Error: /,""),l=c?c.stack.replace(/^Error: /,"").replace(e+"\n","").replace(/^ +/gm,"").replace(/^at /gm,"").replace(/(?: \(|@)http.+\/([^/)]+)\)?(?:\n|$)/gm,"@$1\n").replace(/ *\(eval code(:\d+:\d+)\)(?:\n|$)/gm,"@???$1\n"):r+":"+(n=n||"?")+":"+(a=a||"?");return(e+"\n"+l).substr(0,150)}.apply(null,arguments),exFatal:!0})}</script><script nomodule="" src="generated/ie-polyfills.min.js"></script><link rel="stylesheet" href="styles.10ba6936decaea45eabb.css"><style>@media screen and (-ms-high-contrast:active){.mat-toolbar{outline:solid 1px}}.mat-toolbar-row,.mat-toolbar-single-row{display:flex;box-sizing:border-box;padding:0 16px;width:100%;flex-direction:row;align-items:center;white-space:nowrap}.mat-toolbar-multiple-rows{display:flex;box-sizing:border-box;flex-direction:column;width:100%}.mat-toolbar-multiple-rows{min-height:64px}.mat-toolbar-row,.mat-toolbar-single-row{height:64px}@media (max-width:599px){.mat-toolbar-multiple-rows{min-height:56px}.mat-toolbar-row,.mat-toolbar-single-row{height:56px}}</style><style>.mat-icon{background-repeat:no-repeat;display:inline-block;fill:currentColor;height:24px;width:24px}.mat-icon.mat-icon-inline{font-size:inherit;height:inherit;line-height:inherit;width:inherit}[dir=rtl] .mat-icon-rtl-mirror{transform:scale(-1,1)}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon{display:block}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button .mat-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button .mat-icon{margin:auto}</style><style>.mat-button .mat-button-focus-overlay,.mat-icon-button .mat-button-focus-overlay{opacity:0}.mat-button:hover .mat-button-focus-overlay,.mat-stroked-button:hover .mat-button-focus-overlay{opacity:.04}@media (hover:none){.mat-button:hover .mat-button-focus-overlay,.mat-stroked-button:hover .mat-button-focus-overlay{opacity:0}}.mat-button,.mat-flat-button,.mat-icon-button,.mat-stroked-button{box-sizing:border-box;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;outline:0;border:none;-webkit-tap-highlight-color:transparent;display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible}.mat-button::-moz-focus-inner,.mat-flat-button::-moz-focus-inner,.mat-icon-button::-moz-focus-inner,.mat-stroked-button::-moz-focus-inner{border:0}.mat-button[disabled],.mat-flat-button[disabled],.mat-icon-button[disabled],.mat-stroked-button[disabled]{cursor:default}.mat-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-button.cdk-program-focused .mat-button-focus-overlay,.mat-flat-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-flat-button.cdk-program-focused .mat-button-focus-overlay,.mat-icon-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-icon-button.cdk-program-focused .mat-button-focus-overlay,.mat-stroked-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-stroked-button.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-button::-moz-focus-inner,.mat-flat-button::-moz-focus-inner,.mat-icon-button::-moz-focus-inner,.mat-stroked-button::-moz-focus-inner{border:0}.mat-raised-button{box-sizing:border-box;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;outline:0;border:none;-webkit-tap-highlight-color:transparent;display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible;transform:translate3d(0,0,0);transition:background .4s cubic-bezier(.25,.8,.25,1),box-shadow 280ms cubic-bezier(.4,0,.2,1)}.mat-raised-button::-moz-focus-inner{border:0}.mat-raised-button[disabled]{cursor:default}.mat-raised-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-raised-button.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-raised-button::-moz-focus-inner{border:0}._mat-animation-noopable.mat-raised-button{transition:none;animation:none}.mat-stroked-button{border:1px solid currentColor;padding:0 15px;line-height:34px}.mat-fab{box-sizing:border-box;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;outline:0;border:none;-webkit-tap-highlight-color:transparent;display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible;transform:translate3d(0,0,0);transition:background .4s cubic-bezier(.25,.8,.25,1),box-shadow 280ms cubic-bezier(.4,0,.2,1);min-width:0;border-radius:50%;width:56px;height:56px;padding:0;flex-shrink:0}.mat-fab::-moz-focus-inner{border:0}.mat-fab[disabled]{cursor:default}.mat-fab.cdk-keyboard-focused .mat-button-focus-overlay,.mat-fab.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-fab::-moz-focus-inner{border:0}._mat-animation-noopable.mat-fab{transition:none;animation:none}.mat-fab .mat-button-wrapper{padding:16px 0;display:inline-block;line-height:24px}.mat-mini-fab{box-sizing:border-box;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;outline:0;border:none;-webkit-tap-highlight-color:transparent;display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible;transform:translate3d(0,0,0);transition:background .4s cubic-bezier(.25,.8,.25,1),box-shadow 280ms cubic-bezier(.4,0,.2,1);min-width:0;border-radius:50%;width:40px;height:40px;padding:0;flex-shrink:0}.mat-mini-fab::-moz-focus-inner{border:0}.mat-mini-fab[disabled]{cursor:default}.mat-mini-fab.cdk-keyboard-focused .mat-button-focus-overlay,.mat-mini-fab.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-mini-fab::-moz-focus-inner{border:0}._mat-animation-noopable.mat-mini-fab{transition:none;animation:none}.mat-mini-fab .mat-button-wrapper{padding:8px 0;display:inline-block;line-height:24px}.mat-icon-button{padding:0;min-width:0;width:40px;height:40px;flex-shrink:0;line-height:40px;border-radius:50%}.mat-icon-button .mat-icon,.mat-icon-button i{line-height:24px}.mat-button-focus-overlay,.mat-button-ripple.mat-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none;border-radius:inherit}.mat-button-focus-overlay{border-radius:inherit;opacity:0;transition:opacity .2s cubic-bezier(.35,0,.25,1),background-color .2s cubic-bezier(.35,0,.25,1)}._mat-animation-noopable .mat-button-focus-overlay{transition:none}@media screen and (-ms-high-contrast:active){.mat-button-focus-overlay{background-color:rgba(255,255,255,.5)}}.mat-button-ripple-round{border-radius:50%;z-index:1}.mat-button .mat-button-wrapper>*,.mat-fab .mat-button-wrapper>*,.mat-flat-button .mat-button-wrapper>*,.mat-icon-button .mat-button-wrapper>*,.mat-mini-fab .mat-button-wrapper>*,.mat-raised-button .mat-button-wrapper>*,.mat-stroked-button .mat-button-wrapper>*{vertical-align:middle}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button{display:block;font-size:inherit;width:2.5em;height:2.5em}@media screen and (-ms-high-contrast:active){.mat-button,.mat-fab,.mat-flat-button,.mat-icon-button,.mat-mini-fab,.mat-raised-button{outline:solid 1px}}</style><style>.mat-drawer-container{position:relative;z-index:1;box-sizing:border-box;-webkit-overflow-scrolling:touch;display:block;overflow:hidden}.mat-drawer-container[fullscreen]{top:0;left:0;right:0;bottom:0;position:absolute}.mat-drawer-container[fullscreen].mat-drawer-opened{overflow:hidden}.mat-drawer-container.mat-drawer-container-explicit-backdrop .mat-drawer-side{z-index:3}.mat-drawer-container.ng-animate-disabled .mat-drawer-backdrop,.mat-drawer-container.ng-animate-disabled .mat-drawer-content,.ng-animate-disabled .mat-drawer-container .mat-drawer-backdrop,.ng-animate-disabled .mat-drawer-container .mat-drawer-content{transition:none}.mat-drawer-backdrop{top:0;left:0;right:0;bottom:0;position:absolute;display:block;z-index:3;visibility:hidden}.mat-drawer-backdrop.mat-drawer-shown{visibility:visible}.mat-drawer-transition .mat-drawer-backdrop{transition-duration:.4s;transition-timing-function:cubic-bezier(.25,.8,.25,1);transition-property:background-color,visibility}@media screen and (-ms-high-contrast:active){.mat-drawer-backdrop{opacity:.5}}.mat-drawer-content{position:relative;z-index:1;display:block;height:100%;overflow:auto}.mat-drawer-transition .mat-drawer-content{transition-duration:.4s;transition-timing-function:cubic-bezier(.25,.8,.25,1);transition-property:transform,margin-left,margin-right}.mat-drawer{position:relative;z-index:4;display:block;position:absolute;top:0;bottom:0;z-index:3;outline:0;box-sizing:border-box;overflow-y:auto;transform:translate3d(-100%,0,0)}@media screen and (-ms-high-contrast:active){.mat-drawer,[dir=rtl] .mat-drawer.mat-drawer-end{border-right:solid 1px currentColor}}@media screen and (-ms-high-contrast:active){.mat-drawer.mat-drawer-end,[dir=rtl] .mat-drawer{border-left:solid 1px currentColor;border-right:none}}.mat-drawer.mat-drawer-side{z-index:2}.mat-drawer.mat-drawer-end{right:0;transform:translate3d(100%,0,0)}[dir=rtl] .mat-drawer{transform:translate3d(100%,0,0)}[dir=rtl] .mat-drawer.mat-drawer-end{left:0;right:auto;transform:translate3d(-100%,0,0)}.mat-drawer-inner-container{width:100%;height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.mat-sidenav-fixed{position:fixed}</style><style>.nav-link.highlight[_ngcontent-c5]{color:#ff0}</style><style>.mat-progress-bar{display:block;height:4px;overflow:hidden;position:relative;transition:opacity 250ms linear;width:100%}._mat-animation-noopable.mat-progress-bar{transition:none;animation:none}.mat-progress-bar .mat-progress-bar-element,.mat-progress-bar .mat-progress-bar-fill::after{height:100%;position:absolute;width:100%}.mat-progress-bar .mat-progress-bar-background{width:calc(100% + 10px)}@media screen and (-ms-high-contrast:active){.mat-progress-bar .mat-progress-bar-background{display:none}}.mat-progress-bar .mat-progress-bar-buffer{transform-origin:top left;transition:transform 250ms ease}@media screen and (-ms-high-contrast:active){.mat-progress-bar .mat-progress-bar-buffer{border-top:solid 5px;opacity:.5}}.mat-progress-bar .mat-progress-bar-secondary{display:none}.mat-progress-bar .mat-progress-bar-fill{animation:none;transform-origin:top left;transition:transform 250ms ease}@media screen and (-ms-high-contrast:active){.mat-progress-bar .mat-progress-bar-fill{border-top:solid 4px}}.mat-progress-bar .mat-progress-bar-fill::after{animation:none;content:'';display:inline-block;left:0}.mat-progress-bar[dir=rtl],[dir=rtl] .mat-progress-bar{transform:rotateY(180deg)}.mat-progress-bar[mode=query]{transform:rotateZ(180deg)}.mat-progress-bar[mode=query][dir=rtl],[dir=rtl] .mat-progress-bar[mode=query]{transform:rotateZ(180deg) rotateY(180deg)}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-fill,.mat-progress-bar[mode=query] .mat-progress-bar-fill{transition:none}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-primary,.mat-progress-bar[mode=query] .mat-progress-bar-primary{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-primary-indeterminate-translate 2s infinite linear;left:-145.166611%}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-primary.mat-progress-bar-fill::after,.mat-progress-bar[mode=query] .mat-progress-bar-primary.mat-progress-bar-fill::after{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-primary-indeterminate-scale 2s infinite linear}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-secondary,.mat-progress-bar[mode=query] .mat-progress-bar-secondary{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-secondary-indeterminate-translate 2s infinite linear;left:-54.888891%;display:block}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-secondary.mat-progress-bar-fill::after,.mat-progress-bar[mode=query] .mat-progress-bar-secondary.mat-progress-bar-fill::after{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-secondary-indeterminate-scale 2s infinite linear}.mat-progress-bar[mode=buffer] .mat-progress-bar-background{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-background-scroll 250ms infinite linear;display:block}.mat-progress-bar._mat-animation-noopable .mat-progress-bar-background,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-buffer,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-fill,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-fill::after,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-primary,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-primary.mat-progress-bar-fill::after,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-secondary,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-secondary.mat-progress-bar-fill::after{animation:none;transition:none}@keyframes mat-progress-bar-primary-indeterminate-translate{0%{transform:translateX(0)}20%{animation-timing-function:cubic-bezier(.5,0,.70173,.49582);transform:translateX(0)}59.15%{animation-timing-function:cubic-bezier(.30244,.38135,.55,.95635);transform:translateX(83.67142%)}100%{transform:translateX(200.61106%)}}@keyframes mat-progress-bar-primary-indeterminate-scale{0%{transform:scaleX(.08)}36.65%{animation-timing-function:cubic-bezier(.33473,.12482,.78584,1);transform:scaleX(.08)}69.15%{animation-timing-function:cubic-bezier(.06,.11,.6,1);transform:scaleX(.66148)}100%{transform:scaleX(.08)}}@keyframes mat-progress-bar-secondary-indeterminate-translate{0%{animation-timing-function:cubic-bezier(.15,0,.51506,.40969);transform:translateX(0)}25%{animation-timing-function:cubic-bezier(.31033,.28406,.8,.73371);transform:translateX(37.65191%)}48.35%{animation-timing-function:cubic-bezier(.4,.62704,.6,.90203);transform:translateX(84.38617%)}100%{transform:translateX(160.27778%)}}@keyframes mat-progress-bar-secondary-indeterminate-scale{0%{animation-timing-function:cubic-bezier(.15,0,.51506,.40969);transform:scaleX(.08)}19.15%{animation-timing-function:cubic-bezier(.31033,.28406,.8,.73371);transform:scaleX(.4571)}44.15%{animation-timing-function:cubic-bezier(.4,.62704,.6,.90203);transform:scaleX(.72796)}100%{transform:scaleX(.08)}}@keyframes mat-progress-bar-background-scroll{to{transform:translateX(-8px)}}</style><script charset="utf-8" src="toc-toc-module-ngfactory.36694c537d7ff0b2c081.js"></script><script charset="utf-8" src="default~code-code-example-module-ngfactory~code-code-tabs-module-ngfactory~getting-started-ng-for-ng~82f414e5.c64b8abba8f3b7168616.js"></script><script charset="utf-8" src="default~code-code-example-module-ngfactory~code-code-tabs-module-ngfactory.98830c4eaea3268880de.js"></script><script charset="utf-8" src="code-code-example-module-ngfactory.bb14a03c7095ffd0e7bd.js"></script><script charset="utf-8" src="code-code-tabs-module-ngfactory.e688ffb25bac166802a3.js"></script><script charset="utf-8" src="live-example-live-example-module-ngfactory.fef451c7b16613f97732.js"></script><script charset="utf-8" src="21.d3ebf64aa7b6d3a4b8f6.js"></script><style>.mat-card{transition:box-shadow 280ms cubic-bezier(.4,0,.2,1);display:block;position:relative;padding:16px;border-radius:4px}.mat-card .mat-divider-horizontal{position:absolute;left:0;width:100%}[dir=rtl] .mat-card .mat-divider-horizontal{left:auto;right:0}.mat-card .mat-divider-horizontal.mat-divider-inset{position:static;margin:0}[dir=rtl] .mat-card .mat-divider-horizontal.mat-divider-inset{margin-right:0}@media screen and (-ms-high-contrast:active){.mat-card{outline:solid 1px}}.mat-card-actions,.mat-card-content,.mat-card-subtitle{display:block;margin-bottom:16px}.mat-card-title{display:block;margin-bottom:8px}.mat-card-actions{margin-left:-8px;margin-right:-8px;padding:8px 0}.mat-card-actions-align-end{display:flex;justify-content:flex-end}.mat-card-image{width:calc(100% + 32px);margin:0 -16px 16px -16px}.mat-card-footer{display:block;margin:0 -16px -16px -16px}.mat-card-actions .mat-button,.mat-card-actions .mat-raised-button{margin:0 8px}.mat-card-header{display:flex;flex-direction:row}.mat-card-header .mat-card-title{margin-bottom:12px}.mat-card-header-text{margin:0 16px}.mat-card-avatar{height:40px;width:40px;border-radius:50%;flex-shrink:0;object-fit:cover}.mat-card-title-group{display:flex;justify-content:space-between}.mat-card-sm-image{width:80px;height:80px}.mat-card-md-image{width:112px;height:112px}.mat-card-lg-image{width:152px;height:152px}.mat-card-xl-image{width:240px;height:240px;margin:-8px}.mat-card-title-group>.mat-card-xl-image{margin:-8px 0 8px 0}@media (max-width:599px){.mat-card-title-group{margin:0}.mat-card-xl-image{margin-left:0;margin-right:0}}.mat-card-content>:first-child,.mat-card>:first-child{margin-top:0}.mat-card-content>:last-child:not(.mat-card-footer),.mat-card>:last-child:not(.mat-card-footer){margin-bottom:0}.mat-card-image:first-child{margin-top:-16px;border-top-left-radius:inherit;border-top-right-radius:inherit}.mat-card>.mat-card-actions:last-child{margin-bottom:-8px;padding-bottom:0}.mat-card-actions .mat-button:first-child,.mat-card-actions .mat-raised-button:first-child{margin-left:0;margin-right:0}.mat-card-title{margin-bottom:8px}.mat-card-subtitle:not(:first-child),.mat-card-title:not(:first-child){margin-top:-4px}.mat-card-header .mat-card-subtitle:not(:first-child){margin-top:-8px}.mat-card>.mat-card-xl-image:first-child{margin-top:-8px}.mat-card>.mat-card-xl-image:last-child{margin-bottom:-8px}</style><style>.mat-tab-group{display:flex;flex-direction:column}.mat-tab-group.mat-tab-group-inverted-header{flex-direction:column-reverse}.mat-tab-label{height:48px;padding:0 24px;cursor:pointer;box-sizing:border-box;opacity:.6;min-width:160px;text-align:center;display:inline-flex;justify-content:center;align-items:center;white-space:nowrap;position:relative}.mat-tab-label:focus{outline:0}.mat-tab-label:focus:not(.mat-tab-disabled){opacity:1}@media screen and (-ms-high-contrast:active){.mat-tab-label:focus{outline:dotted 2px}}.mat-tab-label.mat-tab-disabled{cursor:default}@media screen and (-ms-high-contrast:active){.mat-tab-label.mat-tab-disabled{opacity:.5}}.mat-tab-label .mat-tab-label-content{display:inline-flex;justify-content:center;align-items:center;white-space:nowrap}@media screen and (-ms-high-contrast:active){.mat-tab-label{opacity:1}}@media (max-width:599px){.mat-tab-label{padding:0 12px}}@media (max-width:959px){.mat-tab-label{padding:0 12px}}.mat-tab-group[mat-stretch-tabs]>.mat-tab-header .mat-tab-label{flex-basis:0;flex-grow:1}.mat-tab-body-wrapper{position:relative;overflow:hidden;display:flex;transition:height .5s cubic-bezier(.35,0,.25,1)}.mat-tab-body{top:0;left:0;right:0;bottom:0;position:absolute;display:block;overflow:hidden;flex-basis:100%}.mat-tab-body.mat-tab-body-active{position:relative;overflow-x:hidden;overflow-y:auto;z-index:1;flex-grow:1}.mat-tab-group.mat-tab-group-dynamic-height .mat-tab-body.mat-tab-body-active{overflow-y:hidden}</style><style>.mat-tab-header{display:flex;overflow:hidden;position:relative;flex-shrink:0}.mat-tab-label{height:48px;padding:0 24px;cursor:pointer;box-sizing:border-box;opacity:.6;min-width:160px;text-align:center;display:inline-flex;justify-content:center;align-items:center;white-space:nowrap;position:relative}.mat-tab-label:focus{outline:0}.mat-tab-label:focus:not(.mat-tab-disabled){opacity:1}@media screen and (-ms-high-contrast:active){.mat-tab-label:focus{outline:dotted 2px}}.mat-tab-label.mat-tab-disabled{cursor:default}@media screen and (-ms-high-contrast:active){.mat-tab-label.mat-tab-disabled{opacity:.5}}.mat-tab-label .mat-tab-label-content{display:inline-flex;justify-content:center;align-items:center;white-space:nowrap}@media screen and (-ms-high-contrast:active){.mat-tab-label{opacity:1}}@media (max-width:599px){.mat-tab-label{min-width:72px}}.mat-ink-bar{position:absolute;bottom:0;height:2px;transition:.5s cubic-bezier(.35,0,.25,1)}.mat-tab-group-inverted-header .mat-ink-bar{bottom:auto;top:0}@media screen and (-ms-high-contrast:active){.mat-ink-bar{outline:solid 2px;height:0}}.mat-tab-header-pagination{position:relative;display:none;justify-content:center;align-items:center;min-width:32px;cursor:pointer;z-index:2}.mat-tab-header-pagination-controls-enabled .mat-tab-header-pagination{display:flex}.mat-tab-header-pagination-before,.mat-tab-header-rtl .mat-tab-header-pagination-after{padding-left:4px}.mat-tab-header-pagination-before .mat-tab-header-pagination-chevron,.mat-tab-header-rtl .mat-tab-header-pagination-after .mat-tab-header-pagination-chevron{transform:rotate(-135deg)}.mat-tab-header-pagination-after,.mat-tab-header-rtl .mat-tab-header-pagination-before{padding-right:4px}.mat-tab-header-pagination-after .mat-tab-header-pagination-chevron,.mat-tab-header-rtl .mat-tab-header-pagination-before .mat-tab-header-pagination-chevron{transform:rotate(45deg)}.mat-tab-header-pagination-chevron{border-style:solid;border-width:2px 2px 0 0;content:'';height:8px;width:8px}.mat-tab-header-pagination-disabled{box-shadow:none;cursor:default}.mat-tab-label-container{display:flex;flex-grow:1;overflow:hidden;z-index:1}.mat-tab-list{flex-grow:1;position:relative;transition:transform .5s cubic-bezier(.35,0,.25,1)}.mat-tab-labels{display:flex}[mat-align-tabs=center] .mat-tab-labels{justify-content:center}[mat-align-tabs=end] .mat-tab-labels{justify-content:flex-end}</style><style>.mat-tab-body-content{height:100%;overflow:auto}.mat-tab-group-dynamic-height .mat-tab-body-content{overflow:hidden}</style></head><body><aio-shell ng-version="7.0.0" class="mode-stable sidenav-open page-tutorial-toh-pt6 folder-tutorial view-SideNav aio-notification-show"><div id="top-of-page"></div><mat-toolbar class="app-toolbar no-print mat-toolbar mat-primary mat-toolbar-multiple-rows" color="primary"><mat-toolbar-row class="notification-container mat-toolbar-row"><aio-notification expirationdate="2019-03-01" notificationid="survey-february-2019" class="ng-tns-c2-0 ng-trigger ng-trigger-hideAnimation"><span class="content"><a href="http://bit.ly/angular-survey-2019" target="_blank"><mat-icon aria-label="Announcement" class="icon mat-icon" role="img" svgicon="insert_comment" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></mat-icon><span class="message"><b>填写这份《一分钟调查》</b>，帮我们（开发组）做得更好！</span><span class="action-button">去填写</span></a></span><button class="close-button mat-icon-button" aria-label="Close" mat-icon-button=""><span class="mat-button-wrapper"><mat-icon aria-label="Dismiss notification" class="mat-icon ng-tns-c2-0" role="img" svgicon="close" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></mat-icon></span><div class="mat-button-ripple mat-ripple mat-button-ripple-round" matripple=""></div><div class="mat-button-focus-overlay"></div></button></aio-notification></mat-toolbar-row><mat-toolbar-row class="mat-toolbar-row"><button class="hamburger mat-button" mat-button="" title="Docs menu"><span class="mat-button-wrapper"><mat-icon class="mat-icon" role="img" svgicon="menu" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path></svg></mat-icon></span><div class="mat-button-ripple mat-ripple" matripple=""></div><div class="mat-button-focus-overlay"></div></button><a class="nav-link home" href="/"><img alt="Home" height="40" src="assets/images/logos/angular/logo-nav@2x.png" title="Home" width="150" class="ng-star-inserted"></a><aio-top-menu _nghost-c5="" class="ng-star-inserted"><ul _ngcontent-c5="" role="navigation"><li _ngcontent-c5="" class="ng-star-inserted"><a _ngcontent-c5="" class="nav-link" href="features" title="特性"><span _ngcontent-c5="" class="nav-link-inner">特性</span></a></li><li _ngcontent-c5="" class="ng-star-inserted"><a _ngcontent-c5="" class="nav-link" href="docs" title="文档"><span _ngcontent-c5="" class="nav-link-inner">文档</span></a></li><li _ngcontent-c5="" class="ng-star-inserted"><a _ngcontent-c5="" class="nav-link" href="resources" title="资源"><span _ngcontent-c5="" class="nav-link-inner">资源</span></a></li><li _ngcontent-c5="" class="ng-star-inserted"><a _ngcontent-c5="" class="nav-link" href="events" title="会议"><span _ngcontent-c5="" class="nav-link-inner">会议</span></a></li><li _ngcontent-c5="" class="ng-star-inserted"><a _ngcontent-c5="" class="nav-link" href="https://blog.angular.io/" title="博客"><span _ngcontent-c5="" class="nav-link-inner">博客</span></a></li><li _ngcontent-c5="" class="ng-star-inserted"><a _ngcontent-c5="" class="nav-link" href="translations/cn/home" title="关于中文版"><span _ngcontent-c5="" class="nav-link-inner">关于中文版</span></a></li></ul></aio-top-menu><aio-search-box class="search-container"><input aria-label="search" placeholder="搜索" type="search"></aio-search-box><div class="toolbar-external-icons-container"><a aria-label="Angular on twitter" href="https://twitter.com/angular" title="Twitter"><mat-icon class="mat-icon" role="img" svgicon="logos:twitter" aria-hidden="true"><svg focusable="false" viewBox="0 0 50 59" xmlns="http://www.w3.org/2000/svg"><path d="M50,9.3c-1.8,0.8-3.8,1.4-5.9,1.6c2.1-1.3,3.7-3.3,4.5-5.7c-2,1.2-4.2,2-6.5,2.5c-1.9-2-4.5-3.2-7.5-3.2c-5.7,0-10.3,4.6-10.3,10.3c0,0.8,0.1,1.6,0.3,2.3C16.1,16.7,8.5,12.6,3.5,6.4c-0.9,1.5-1.4,3.3-1.4,5.2c0,3.6,1.8,6.7,4.6,8.5C5,20,3.4,19.6,2,18.8c0,0,0,0.1,0,0.1c0,5,3.5,9.1,8.2,10.1c-0.9,0.2-1.8,0.4-2.7,0.4c-0.7,0-1.3-0.1-1.9-0.2c1.3,4.1,5.1,7,9.6,7.1c-3.5,2.8-7.9,4.4-12.7,4.4c-0.8,0-1.6,0-2.4-0.1c4.5,2.9,9.9,4.6,15.7,4.6c18.9,0,29.2-15.6,29.2-29.2c0-0.4,0-0.9,0-1.3C46.9,13.2,48.6,11.4,50,9.3z"></path></svg></mat-icon></a><a aria-label="Angular on github" href="https://github.com/angular/angular" title="GitHub"><mat-icon class="mat-icon" role="img" svgicon="logos:github" aria-hidden="true"><svg focusable="false" viewBox="0 0 51.8 50.4" xmlns="http://www.w3.org/2000/svg"><path d="M25.9,0.2C11.8,0.2,0.3,11.7,0.3,25.8c0,11.3,7.3,20.9,17.5,24.3c1.3,0.2,1.7-0.6,1.7-1.2c0-0.6,0-2.6,0-4.8c-7.1,1.5-8.6-3-8.6-3c-1.2-3-2.8-3.7-2.8-3.7c-2.3-1.6,0.2-1.6,0.2-1.6c2.6,0.2,3.9,2.6,3.9,2.6c2.3,3.9,6,2.8,7.5,2.1c0.2-1.7,0.9-2.8,1.6-3.4c-5.7-0.6-11.7-2.8-11.7-12.7c0-2.8,1-5.1,2.6-6.9c-0.3-0.7-1.1-3.3,0.3-6.8c0,0,2.1-0.7,7,2.6c2-0.6,4.2-0.9,6.4-0.9c2.2,0,4.4,0.3,6.4,0.9c4.9-3.3,7-2.6,7-2.6c1.4,3.5,0.5,6.1,0.3,6.8c1.6,1.8,2.6,4.1,2.6,6.9c0,9.8-6,12-11.7,12.6c0.9,0.8,1.7,2.4,1.7,4.7c0,3.4,0,6.2,0,7c0,0.7,0.5,1.5,1.8,1.2c10.2-3.4,17.5-13,17.5-24.3C51.5,11.7,40.1,0.2,25.9,0.2z"></path></svg></mat-icon></a></div></mat-toolbar-row></mat-toolbar><mat-sidenav-container class="sidenav-container mat-drawer-container mat-sidenav-container mat-drawer-transition has-floating-toc" role="main"><div class="mat-drawer-backdrop ng-star-inserted"></div><div class="cdk-visually-hidden cdk-focus-trap-anchor"></div><mat-sidenav class="sidenav mat-drawer mat-sidenav ng-tns-c7-1 ng-trigger ng-trigger-transform mat-drawer-side ng-star-inserted" tabindex="-1" style="transform:none;visibility:visible"><div class="mat-drawer-inner-container"><aio-nav-menu><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="guide/quickstart" title="对 Angular 和 Angular CLI 基础知识的简短介绍" target="_self">快速上手</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-1 expanded selected ng-star-inserted" type="button" title="此《英雄指南》教程会带你用 TypeScript 一步步创建一个 Angular 应用。" aria-pressed="true">教程<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 expanded selected"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="tutorial" title="《英雄指南》教程简介" target="_self">简介</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt0" title="创建应用的外壳" target="_self">应用的“外壳”</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt1" title="第一部分：构建一个简单的英雄编辑器" target="_self">1. 英雄编辑器</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt2" title="第二部分：构建一个主从结构的页面，用于展现英雄列表。" target="_self">2. 显示英雄列表</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt3" title="第三部分：把主从结构的页面重构成多个组件。" target="_self">3. 主从组件</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt4" title="第四部分：创建一个可复用的服务来管理英雄数据。" target="_self">4. 服务</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt5" title="第五部分：添加 Angular 路由器，并且学习在视图之间导航。" target="_self">5. 路由</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 expanded selected ng-star-inserted" style="position:relative" href="tutorial/toh-pt6" title="第六部分：通过 HTTP 来获取并保存英雄数据。" target="_self">6. HTTP</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="学习 Angular 的核心知识" aria-pressed="false">核心知识<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular 应用的基本构造块。" aria-pressed="false">架构<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture" title="Angular 应用的基本构造块" target="_self">架构概览</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture-modules" title="关于模块。" target="_self">模块（NgModule）简介</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture-components" title="关于组件、模板和视图。" target="_self">组件简介</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture-services" title="关于服务与依赖注入。" target="_self">服务与 DI 简介</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture-next-steps" title="学完基础知识之后……" target="_self">后续步骤</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="使用数据绑定构建动态视图" aria-pressed="false">组件与模板<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/displaying-data" title="属性绑定可以帮助应用把数据显示在界面上" target="_self">显示数据</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/template-syntax" title="学习如何写模板，以便借助数据绑定机制显示数据并响应事件。" target="_self">模板语法</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/user-input" title="用户输入会触发 DOM 事件。Angular 会通过事件绑定来监听那些事件，并把修改后的值传回应用的组件和模型中。" target="_self">用户输入</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/lifecycle-hooks" title="Angular 调用指令和组件的生命周期钩子函数，包括它的创建、变更和销毁时。" target="_self">生命周期钩子</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/component-interaction" title="在不同的指令和组件之间共享信息" target="_self">组件交互</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/component-styles" title="添加专属于某个组件的样式" target="_self">组件样式</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/elements" title="把组件转换成自定义元素。" target="_self">Angular 自定义元素</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dynamic-component-loader" title="动态加载组件" target="_self">动态组件</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/attribute-directives" title="属性型指令把行为添加到现有元素上。" target="_self">属性型指令</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/structural-directives" title="结构型指令可以操纵页面的布局" target="_self">结构型指令</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/pipes" title="管道可以在模板中转换显示的内容。" target="_self">管道</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular 的表单" aria-pressed="false">表单<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/forms-overview" title="表单可以创建集中、高效、引人注目的输入体验。Angular 表单可以协调一组数据绑定控件，跟踪变更，验证输入，并表达错误信息。" target="_self">简介</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/reactive-forms" title="使用 FormBuilder、表单组和表单数组创建响应式表单。" target="_self">响应式表单</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/forms" title="使用指令和 Angular 模板语法创建模板驱动表单。" target="_self">模板驱动表单</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/form-validation" title="验证用户的表单输入" target="_self">表单验证</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dynamic-form" title="使用 FormGroup 渲染动态表单。" target="_self">动态表单</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Observable 与 RxJS" aria-pressed="false">Observable 与 RxJS<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/observables" title="" target="_self">可观察对象(Observable)</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/rx-library" title="" target="_self">RxJS 库</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/observables-in-angular" title="" target="_self">Angular 中的可观察对象</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/practical-observable-usage" title="" target="_self">用法实战</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/comparing-observables" title="" target="_self">与其它技术的比较</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/bootstrapping" title="在应用的根模块（AppModule）中告诉 Angular 如何构造并引导引用。" target="_self">引导启动</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular 中的模块" aria-pressed="false">NgModule<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ngmodules" title="使用 NgModule 让你的应用更高效" target="_self">NgModule 简介</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ngmodule-vs-jsmodule" title="JavaScript 模块和 NgModule 之间的差异" target="_self">JS 模块 vs NgModule</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/frequent-ngmodules" title="介绍最常用的 NgModule" target="_self">常用模块</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/module-types" title="介绍特性模块的几种类型" target="_self">特性模块的分类</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/entry-components" title="关于 Angular 中入口组件的一切" target="_self">入口组件</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/feature-modules" title="创建特性模块，以组织你的代码" target="_self">特性模块</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/providers" title="服务提供商与 NgModule" target="_self">服务提供商</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/singleton-services" title="创建单例服务" target="_self">单例服务</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/lazy-loading-ngmodules" title="惰性加载模块，以提高应用的性能" target="_self">惰性加载的特性模块</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/sharing-ngmodules" title="共享 NgModule 让你的应用现代化。" target="_self">共享 NgModule</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ngmodule-api" title="理解 NgModule 的那些细节。" target="_self">NgModule API</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ngmodule-faq" title="回答关于 NgModules 的常见问题。" target="_self">NgModule 常见问题</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="依赖注入：创建并注入各种服务。" aria-pressed="false">依赖注入<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dependency-injection" title="Angular 的依赖注入系统能够为 Angular 创建的类创建并交付它们所依赖的服务。" target="_self">Angular 依赖注入</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/hierarchical-dependency-injection" title="与组件树平行的注入器树，并支持嵌套的依赖。" target="_self">多级注入器</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dependency-injection-providers" title="各种提供商类型的更多知识。" target="_self">DI 提供商</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dependency-injection-in-action" title="依赖注入的使用技巧" target="_self">DI 实战</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dependency-injection-navtree" title="使用注入器树来查找父组件。" target="_self">浏览组件树</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/http" title="通过 HTTP 协议与远程服务器对话。" target="_self">HttpClient</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/router" title="揭示如何通过 Angular 路由进行基本的屏幕导航。" target="_self">路由与导航</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular 动画系统指南" aria-pressed="false">动画<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/animations" title="Angular 动画的基础技术。" target="_self">简介</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/transition-and-triggers" title="转场与触发器的高级技术。" target="_self">转场与触发器</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/complex-animation-sequences" title="复杂的 Angular 动画序列。" target="_self">复杂序列</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/reusable-animations" title="创建可复用的动画。" target="_self">可复用动画</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/route-animations" title="为路由提供转场动画。" target="_self">路由转场动画</a></div></aio-nav-item></div></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="把 Angular 用到你的实际工作中的一些技巧" aria-pressed="false">其它技术<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/security" title="Angular 应用开发中的安全技术。" target="_self">安全</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/i18n" title="把应用模板中的文本翻译成多种语言。" target="_self">国际化 (i18n)</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="Angular Service Worker: 控制应用资源的缓存。" aria-pressed="false">Service Worker 与 PWA<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-intro" title="Angular 对 Service Worker 的实现提升了慢速或不稳定的网络连接下的用户体验。" target="_self">简介</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-getting-started" title="在 CLI 项目中启用 Service Worker，并在浏览器中查看效果。" target="_self">快速起步</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-communications" title="那些能让你和 Angular 的 Service Worker 通讯的服务类。" target="_self">与 Service Worker 通讯</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-devops" title="使用 Service Worker 运行应用、管理应用更新、调试以及杀掉正在运行的应用。" target="_self">生产环境下的 Service Worker</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-config" title="配置 Service Worker 的缓存行为。" target="_self">Service Worker 配置</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/universal" title="使用 Angular Universal 在服务端渲染 HTML。" target="_self">服务端渲染</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="把 AngularJS 应用增量式的升级到 Angular。" aria-pressed="false">从 AngularJS 升级<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/upgrade" title="把 AngularJS 应用增量式的升级到 Angular。" target="_self">升级步骤</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/upgrade-performance" title="用更灵活的方式把 AngularJS 升级到 Angular" target="_self">更关注性能的升级方式</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ajs-quick-reference" title="学习如何把 AngularJS 的概念映射到 Angular 中。" target="_self">AngularJS 与 Angular 的概念对照</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="使用共享库扩展 Angular" aria-pressed="false">Angular 库开发<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/libraries" title="理解何时以及如何使用和创建库。" target="_self">库概览</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/using-libraries" title="把已发布的库集成进你的应用中。" target="_self">使用已发布的库</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/creating-libraries" title="通过创建、发布和使用你自己的库来扩展 Angular" target="_self">创建库</a></div></aio-nav-item></div></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="关于环境搭建、构建、测试、部署环境与工具的信息。" aria-pressed="false">环境搭建与部署<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/file-structure" title="Angular 工作区在文件系统中是怎样的。" target="_self">项目文件结构</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/workspace-config" title="&quot;angular.json&quot; 包含供 CLI 命令使用的工作区和项目默认配置。" target="_self">工作区配置</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/npm-packages" title="开发期间和运行期间所需的 npm 包的说明。" target="_self">npm 包</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/typescript-configuration" title="给 Angular 开发者的 TypeScript 配置。" target="_self">TypeScript 配置</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/aot-compiler" title="了解为何以及如何使用预先（AOT）编译器。" target="_self">预先（AOT）编译</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/build" title="构建应用及为应用启动开发服务器。" target="_self">构建与运行</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/testing" title="测试 Angular 应用的技巧与实践。" target="_self">测试</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/deployment" title="了解如何部署 Angular 应用。" target="_self">发布</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/browser-support" title="浏览器支持与腻子脚本指南。" target="_self">浏览器支持</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-2 collapsed ng-star-inserted" type="button" title="整合开发环境和工具。" aria-pressed="false">开发工具集成<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/language-service" title="使用 Angular 语言服务加速开发。" target="_self">语言服务</a></div></aio-nav-item></div></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="Angular 的版本发布实践、更新与升级。" aria-pressed="false">发布信息<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/updating" title="如何把 Angular 应用和库升级到最新版本。" target="_self">保持最新</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/releases" title="Angular 的版本、发布、支持、弃用策略与实践。" target="_self">Angular 发布策略与实践</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="Angular 语法、编码、术语汇总。" aria-pressed="false">快捷手册<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/cheatsheet" title="关于 Angular 常用编码技术的快速指南。" target="_self">速查表</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/styleguide" title="写出 Angular 风格的程序" target="_self">风格指南</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/glossary" title="Angular 中最重要的词汇的简要定义。" target="_self">词汇表</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button class="vertical-menu-item heading level-1 collapsed ng-star-inserted" type="button" title="Angular CLI 命令参考手册。" aria-pressed="false">CLI 命令<mat-icon class="rotating-icon mat-icon" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli" title="CLI 工具介绍、命令、语法" target="_self">概览</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/add" title="ng add." target="_self">ng add</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/build" title="ng build." target="_self">ng build</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/config" title="ng config." target="_self">ng config</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/doc" title="ng doc." target="_self">ng doc</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/e2e" title="ng e2e." target="_self">ng e2e</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/generate" title="ng generate." target="_self">ng generate</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/help" title="ng help." target="_self">ng help</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/lint" title="ng lint." target="_self">ng lint</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/new" title="ng new." target="_self">ng new</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/run" title="ng run." target="_self">ng run</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/serve" title="ng serve." target="_self">ng serve</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/test" title="ng test." target="_self">ng test</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/update" title="ng update." target="_self">ng update</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/version" title="ng version." target="_self">ng version</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/xi18n" title="ng xi18n." target="_self">ng xi18n</a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="api" title="关于 Angular 中类和值的详细信息。" target="_self">API 参考手册</a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><div class="mat-divider ng-star-inserted" style="margin:4px 20px;border-top:1px solid #d3d3d3"></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://ng-china.org" title="2018 ngChina 开发者大会" target="_blank">2018 ngChina @ 杭州<mat-icon class="mat-icon material-icons ng-star-inserted" role="img" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://github.com/ng-docs/ng-docs.github.io/issues" title="github 上的中文互助问答区" target="_blank">互助问答<mat-icon class="mat-icon material-icons ng-star-inserted" role="img" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://material.angular.cn" title="Angular Material 组件库的中文文档" target="_blank">官方 Material 组件库<mat-icon class="mat-icon material-icons ng-star-inserted" role="img" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://ng.ant.design/" title="Ant Design 的 Angular 实现，服务于企业级后台产品。" target="_blank">ng-zorro 组件库<mat-icon class="mat-icon material-icons ng-star-inserted" role="img" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://ng.mobile.ant.design/" title="Ant Design Mobile 的 Angular 实现，服务于无线产品。" target="_blank">ng-zorro mobile 组件库<mat-icon class="mat-icon material-icons ng-star-inserted" role="img" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item></aio-nav-menu><div class="doc-version"><aio-select><div class="form-select-menu"><button class="form-select-button"><strong></strong>stable (v8.0.0-beta.4)</button></div></aio-select></div></div></mat-sidenav><div class="cdk-visually-hidden cdk-focus-trap-anchor"></div><mat-sidenav-content cdkscrollable="" class="mat-drawer-content mat-sidenav-content ng-star-inserted" style="margin-left:260px"><main class="sidenav-content" role="main" id="tutorial-toh-pt6"><aio-mode-banner></aio-mode-banner><aio-doc-viewer class=""><div style="opacity:1"><div class="github-links"><a href="https://github.com/angular/angular-cn/edit/aio/aio/content/tutorial/toh-pt6.md?message=docs%3A%20请简述你的修改..." aria-label="提供编辑建议" title="提供编辑建议"><i class="material-icons" aria-hidden="true" role="img">mode_edit</i></a></div><div class="content"><h1 id="http">HTTP<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#http"><i class="material-icons">link</i></a></h1><aio-toc class="embedded" ng-version="7.0.0"><div class="toc-inner no-print collapsed ng-star-inserted"><button aria-label="Expand/collapse contents" class="toc-heading embedded secondary ng-star-inserted" title="Expand/collapse contents" type="button" aria-pressed="false">目录<mat-icon class="rotating-icon mat-icon collapsed" role="img" svgicon="keyboard_arrow_right" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><ul class="toc-list embedded"><li title="启用 HTTP 服务link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#enable-http-services">启用 HTTP 服务</a></li><li title="模拟数据服务器link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#simulate-a-data-server">模拟数据服务器</a></li><li title="英雄与 HTTPlink" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#heroes-and-http">英雄与 HTTP</a></li><li title="通过 HttpClient 获取英雄link" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#get-heroes-with-httpclient">通过 <code>HttpClient</code> 获取英雄</a></li><li title="Http 方法返回单个值link" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#http-methods-return-one-value">Http 方法返回单个值</a></li><li title="HttpClient.get 返回响应数据link" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#httpclientget-returns-response-data"><code>HttpClient.get</code> 返回响应数据</a></li><li title="错误处理link" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#error-handling">错误处理</a></li><li title="窥探 Observablelink" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#tap-into-the-observable">窥探 <code>Observable</code></a></li><li title="通过 id 获取英雄link" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#get-hero-by-id">通过 id 获取英雄</a></li><li title="修改英雄link" class="h2 secondary ng-star-inserted"><a href="tutorial/toh-pt6#update-heroes">修改英雄</a></li><li title="添加新英雄link" class="h2 secondary ng-star-inserted"><a href="tutorial/toh-pt6#add-a-new-hero">添加新英雄</a></li><li title="删除某个英雄link" class="h2 secondary ng-star-inserted"><a href="tutorial/toh-pt6#delete-a-hero">删除某个英雄</a></li><li title="根据名字搜索link" class="h2 secondary ng-star-inserted"><a href="tutorial/toh-pt6#search-by-name">根据名字搜索</a></li><li title="为仪表盘添加搜索功能link" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#add-search-to-the-dashboard">为仪表盘添加搜索功能</a></li><li title="创建 HeroSearchComponentlink" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#create-herosearchcomponent">创建 <code>HeroSearchComponent</code></a></li><li title="AsyncPipelink" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#asyncpipe"><em>AsyncPipe</em></a></li><li title="修正 HeroSearchComponent 类link" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#fix-the-herosearchcomponent-class">修正 <code>HeroSearchComponent</code> 类</a></li><li title="RxJS Subject 类型的 searchTermslink" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#the-searchterms-rxjs-subject">RxJS <code>Subject</code> 类型的 <code>searchTerms</code></a></li><li title="串联 RxJS 操作符link" class="h3 secondary ng-star-inserted"><a href="tutorial/toh-pt6#chaining-rxjs-operators">串联 RxJS 操作符</a></li><li title="查看最终代码link" class="h2 secondary ng-star-inserted"><a href="tutorial/toh-pt6#final-code-review">查看最终代码</a></li><li title="小结link" class="h2 secondary ng-star-inserted"><a href="tutorial/toh-pt6#summary">小结</a></li></ul><button aria-label="Expand/collapse contents" class="toc-more-items embedded material-icons collapsed ng-star-inserted" title="Expand/collapse contents" type="button" aria-pressed="false"></button></div></aio-toc><p translation-result="on">在这节课中，你将借助 Angular 的 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 来添加一些数据持久化特性。</p><p translation-origin="off">In this tutorial, you'll add the following data persistence features with help from Angular's <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code>.</p><ul><li><p translation-result="on"> <code>HeroService</code> 通过 HTTP 请求获取英雄数据。</p><p translation-origin="off">The <code>HeroService</code> gets hero data with HTTP requests.</p></li><li><p translation-result="on">用户可以添加、编辑和删除英雄，并通过 HTTP 来保存这些更改。</p><p translation-origin="off">Users can add, edit, and delete heroes and save these changes over HTTP.</p></li><li><p translation-result="on">用户可以根据名字搜索英雄。</p><p translation-origin="off">Users can search for heroes by name.</p></li></ul><p translation-result="on">当你完成这一章时，应用会变成这样：<live-example ng-version="7.0.0"><span style="display:none"></span><span><span class="ng-star-inserted"><a target="_blank" href="generated/live-examples/toh-pt6/stackblitz.html" title="在线例子">在线例子</a><span class="ng-star-inserted"> / <a download="" title="下载范例" href="generated/zips/toh-pt6/toh-pt6.zip">下载范例</a></span></span></span></live-example>。</p><p translation-origin="off">When you're done with this page, the app should look like this<live-example ng-version="7.0.0"><span style="display:none"></span><span><span class="ng-star-inserted"><a target="_blank" href="generated/live-examples/toh-pt6/stackblitz.html" title="在线例子">在线例子</a><span class="ng-star-inserted"> / <a download="" title="下载范例" href="generated/zips/toh-pt6/toh-pt6.zip">下载范例</a></span></span></span></live-example>.</p><h2 id="enable-http-services" translation-result="on">启用 HTTP 服务<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#enable-http-services"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="enable-http-services">Enable HTTP services<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#enable-http-services"><i class="material-icons">link</i></a></h2><p translation-result="on"><code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 是 Angular 通过 HTTP 与远程服务器通讯的机制。</p><p translation-origin="off"><code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> is Angular's mechanism for communicating with a remote server over HTTP.</p><p translation-result="on">要让 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 在应用中随处可用，请</p><p translation-origin="off">To make <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> available everywhere in the app:</p><ul><li><p translation-result="on">打开根模块 <code>AppModule</code>，</p><p translation-origin="off">open the root <code>AppModule</code> </p></li><li><p translation-result="on">从 <code>@angular/common/<a href="api/common/http" class="code-anchor">http</a></code> 中导入 <code><a href="api/common/http/HttpClientModule" class="code-anchor">HttpClientModule</a></code> 符号，</p><p translation-origin="off">import the <code><a href="api/common/http/HttpClientModule" class="code-anchor">HttpClientModule</a></code> symbol from <code>@angular/common/<a href="api/common/http" class="code-anchor">http</a></code></p></li></ul><code-example path="toh-pt6/src/app/app.module.ts" region="import-http-client" header="src/app/app.module.ts (Http Client import)" ng-version="7.0.0"><div style="display:none">import { <a href="api/common/http/HttpClientModule" class="code-anchor">HttpClientModule</a> } from '@angular/common/<a href="api/common/http" class="code-anchor">http</a>';</div><header class="ng-star-inserted">src/app/app.module.ts (Http Client import)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/app.module.ts (Http Client import)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><a href="api/common/http/HttpClientModule" class="code-anchor"><span class="typ">HttpClientModule</span></a><span class="pln"> </span><span class="pun">}</span><span class="pln">    </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/common/</span><a href="api/common/http" class="code-anchor"><span class="str">http</span></a><span class="str">'</span><span class="pun">;</span></code>
    </pre></aio-code></code-example><ul><li><p translation-result="on">把它加入 <code>@<a href="api/core/NgModule#imports" class="code-anchor">NgModule.imports</a></code> 数组。</p><p translation-origin="off">add it to the <code>@<a href="api/core/NgModule#imports" class="code-anchor">NgModule.imports</a></code> array</p></li></ul><h2 id="simulate-a-data-server" translation-result="on">模拟数据服务器<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#simulate-a-data-server"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="simulate-a-data-server">Simulate a data server<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#simulate-a-data-server"><i class="material-icons">link</i></a></h2><p translation-result="on">这个教学例子会与一个使用 <a href="https://github.com/angular/in-memory-web-api" title="In-memory Web API">内存 Web API（<em>In-memory Web API</em>）</a> 模拟出的远程数据服务器通讯。</p><p translation-origin="off">This tutorial sample <em>mimics</em> communication with a remote data server by using the <a href="https://github.com/angular/in-memory-web-api" title="In-memory Web API"><em>In-memory Web API</em></a> module.</p><p translation-result="on">安装完这个模块之后，应用将会通过 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 来发起请求和接收响应，而不用在乎实际上是这个内存 Web API 在拦截这些请求、操作一个内存数据库，并且给出仿真的响应。</p><p translation-origin="off">After installing the module, the app will make requests to and receive responses from the <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> without knowing that the <em>In-memory Web API</em> is intercepting those requests, applying them to an in-memory data store, and returning simulated responses.</p><p translation-result="on">这给本教程带来了极大的便利。你不用被迫先架设一个服务器再来学习 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code>。</p><p translation-origin="off">This facility is a great convenience for the tutorial. You won't have to set up a server to learn about <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code>.</p><p translation-result="on">在你自己的应用开发的早期阶段这也同样很方便，那时候服务器的 Web API 可能定义上存在错误或者尚未实现。</p><p translation-origin="off">It may also be convenient in the early stages of your own app development when the server's web api is ill-defined or not yet implemented.</p><div class="alert is-important"><p translation-result="on"><strong>重要：</strong> 这个<em>内存 Web API</em> 模块与 Angular 中的 HTTP 模块无关。</p><p translation-origin="off"><strong>Important:</strong> the <em>In-memory Web API</em> module has nothing to do with HTTP in Angular.</p><p translation-result="on">如果你只是在<em>阅读</em>本教程来学习 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code>，那么可以<a href="tutorial/toh-pt6#import-heroes">跳过</a>这一步。 如果你正在随着本教程<em>敲代码</em>，那就留下来，并加上这个<em>内存 Web API</em>。</p><p translation-origin="off">If you're just <em>reading</em> this tutorial to learn about <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code>, you can <a href="tutorial/toh-pt6#import-heroes">skip over</a> this step. If you're <em>coding along</em> with this tutorial, stay here and add the <em>In-memory Web API</em> now.</p></div><p translation-result="on">从 <code>npm</code> 中安装这个<em>内存 Web API</em> 包（译注：请使用 0.5+ 的版本，不要使用 0.4-）</p><p translation-origin="off">Install the <em>In-memory Web API</em> package from <em>npm</em></p><code-example language="sh" class="code-shell" ng-version="7.0.0"><div style="display:none">npm install angular-in-memory-web-api --save</div><aio-code><pre class="prettyprint lang-sh">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">npm install angular</span><span class="pun">-</span><span class="kwd">in</span><span class="pun">-</span><span class="pln">memory</span><span class="pun">-</span><span class="pln">web</span><span class="pun">-</span><span class="pln">api </span><span class="pun">--</span><span class="pln">save</span></code>
    </pre></aio-code></code-example><p translation-result="on">导入 <code>HttpClientInMemoryWebApiModule</code> 和 <code>InMemoryDataService</code> 类（你很快就要创建它）。</p><p translation-origin="off">Import the <code>HttpClientInMemoryWebApiModule</code> and the <code>InMemoryDataService</code> class, which you will create in a moment.</p><code-example path="toh-pt6/src/app/app.module.ts" region="import-in-mem-stuff" header="src/app/app.module.ts (In-memory Web API imports)" ng-version="7.0.0"><div style="display:none">import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; import { InMemoryDataService } from './in-memory-data.service';</div><header class="ng-star-inserted">src/app/app.module.ts (In-memory Web API imports)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/app.module.ts (In-memory Web API imports)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">HttpClientInMemoryWebApiModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'angular-in-memory-web-api'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">InMemoryDataService</span><span class="pln"> </span><span class="pun">}</span><span class="pln">  </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'./in-memory-data.service'</span><span class="pun">;</span></code>
    </pre></aio-code></code-example><p translation-result="on">把 <code>HttpClientInMemoryWebApiModule</code> 添加到 <code>@<a href="api/core/NgModule#imports" class="code-anchor">NgModule.imports</a></code> 数组中（放在 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 之后）， 然后使用 <code>InMemoryDataService</code> 来配置它。</p><p translation-origin="off">Add the <code>HttpClientInMemoryWebApiModule</code> to the <code>@<a href="api/core/NgModule#imports" class="code-anchor">NgModule.imports</a></code> array— <em>after importing the <code><a href="api/common/http/HttpClientModule" class="code-anchor">HttpClientModule</a></code></em>, —while configuring it with the <code>InMemoryDataService</code>.</p><code-example path="toh-pt6/src/app/app.module.ts" region="in-mem-web-api-imports" ng-version="7.0.0"><div style="display:none"><a href="api/common/http/HttpClientModule" class="code-anchor">HttpClientModule</a>, // The HttpClientInMemoryWebApiModule module intercepts HTTP requests // and returns simulated server responses. // Remove it when <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> real server is ready to receive requests. HttpClientInMemoryWebApiModule.forRoot( InMemoryDataService, { dataEncapsulation: false } )</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><a href="api/common/http/HttpClientModule" class="code-anchor"><span class="typ">HttpClientModule</span></a><span class="pun">,</span><span class="pln">

</span><span class="com">// The HttpClientInMemoryWebApiModule module intercepts HTTP requests</span><span class="pln">
</span><span class="com">// and returns simulated server responses.</span><span class="pln">
</span><span class="com">// Remove it when </span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="com">a</span></a><span class="com"> real server is ready to receive requests.</span><span class="pln">
</span><span class="typ">HttpClientInMemoryWebApiModule</span><span class="pun">.</span><span class="pln">forRoot</span><span class="pun">(</span><span class="pln">
  </span><span class="typ">InMemoryDataService</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> dataEncapsulation</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">)</span></code>
    </pre></aio-code></code-example><p translation-result="on"><code><a href="api/router/RouterModule#forRoot" class="code-anchor">forRoot()</a></code> 配置方法接受一个 <code>InMemoryDataService</code> 类（初期的内存数据库）作为参数。</p><p translation-origin="off">The <code><a href="api/router/RouterModule#forRoot" class="code-anchor">forRoot()</a></code> configuration method takes an <code>InMemoryDataService</code> class that primes the in-memory database.</p><p translation-result="on"><code>src/app/in-memory-data.service.ts</code> 类是通过下列命令生成的：</p><p translation-origin="off">The class <code>src/app/in-memory-data.service.ts</code> is generated by the following command:</p><code-example language="sh" class="code-shell" ng-version="7.0.0"><div style="display:none">ng generate service InMemoryData</div><aio-code><pre class="prettyprint lang-sh">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">ng generate service </span><span class="typ">InMemoryData</span></code>
    </pre></aio-code></code-example><p translation-result="on">该类的内容如下：</p><p translation-origin="off">This class has the following content:</p><code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" header="src/app/in-memory-data.service.ts" linenums="false" ng-version="7.0.0"><div style="display:none">import { InMemoryDbService } from 'angular-in-memory-web-api'; import { Hero } from './hero'; import { <a href="api/core/Injectable" class="code-anchor">Injectable</a> } from '@angular/core'; @<a href="api/core/Injectable" class="code-anchor">Injectable</a>({ <a href="api/core/Injectable#providedIn" class="code-anchor">providedIn</a>: 'root', }) export class InMemoryDataService implements InMemoryDbService { createDb() { const heroes = [ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, { id: 13, name: 'Bombasto' }, { id: 14, name: 'Celeritas' }, { id: 15, name: 'Magneta' }, { id: 16, name: 'RubberMan' }, { id: 17, name: 'Dynama' }, { id: 18, name: 'Dr IQ' }, { id: 19, name: 'Magma' }, { id: 20, name: 'Tornado' } ]; return {heroes}; } // Overrides the genId method to ensure that <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> hero always has an id. // If the heroes array is empty, // the method below returns the initial number (11). // if the heroes array is not empty, the method below returns the highest // hero id + 1. genId(heroes: Hero[]): number { return heroes.length &gt; 0 ? Math.max(...heroes.map(hero =&gt; hero.id)) + 1 : 11; } }</div><header class="ng-star-inserted">src/app/in-memory-data.service.ts</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/in-memory-data.service.ts">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">InMemoryDbService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'angular-in-memory-web-api'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Hero</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'./hero'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><a href="api/core/Injectable" class="code-anchor"><span class="typ">Injectable</span></a><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@</span><a href="api/core/Injectable" class="code-anchor"><span class="lit">Injectable</span></a><span class="pun">({</span><span class="pln">
  </span><a href="api/core/Injectable#providedIn" class="code-anchor"><span class="pln">providedIn</span></a><span class="pun">:</span><span class="pln"> </span><span class="str">'root'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">InMemoryDataService</span><span class="pln"> </span><span class="kwd">implements</span><span class="pln"> </span><span class="typ">InMemoryDbService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  createDb</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> heroes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Mr. Nice'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Narco'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">13</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Bombasto'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">14</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Celeritas'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">15</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Magneta'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'RubberMan'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Dynama'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Dr IQ'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">19</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Magma'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Tornado'</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">heroes</span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="com">// Overrides the genId method to ensure that </span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="com">a</span></a><span class="com"> hero always has an id.</span><span class="pln">
  </span><span class="com">// If the heroes array is empty,</span><span class="pln">
  </span><span class="com">// the method below returns the initial number (11).</span><span class="pln">
  </span><span class="com">// if the heroes array is not empty, the method below returns the highest</span><span class="pln">
  </span><span class="com">// hero id + 1.</span><span class="pln">
  genId</span><span class="pun">(</span><span class="pln">heroes</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pun">[]):</span><span class="pln"> number </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> heroes</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">max</span><span class="pun">(...</span><span class="pln">heroes</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">hero </span><span class="pun">=&gt;</span><span class="pln"> hero</span><span class="pun">.</span><span class="pln">id</span><span class="pun">))</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="lit">11</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">这个文件替代了 <code>mock-heroes.ts</code>（你可以安全删除它了）。</p><p translation-origin="off">This file replaces <code>mock-heroes.ts</code>, which is now safe to delete.</p><p translation-result="on">等你真实的服务器就绪时，就可以删除这个<em>内存 Web API</em>，该应用的请求就会直接发给真实的服务器。</p><p translation-origin="off">When your server is ready, detach the <em>In-memory Web API</em>, and the app's requests will go through to the server.</p><p translation-result="on">现在，回来看 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code>。</p><p translation-origin="off">Now back to the <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> story.</p><a id="import-heroes"></a><h2 id="heroes-and-http" translation-result="on">英雄与 HTTP<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#heroes-and-http"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="heroes-and-http">Heroes and HTTP<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#heroes-and-http"><i class="material-icons">link</i></a></h2><p translation-result="on">导入一些所需的 HTTP 符号：</p><p translation-origin="off">Import some HTTP symbols that you'll need:</p><code-example path="toh-pt6/src/app/hero.service.ts" region="import-httpclient" header="src/app/hero.service.ts (import HTTP symbols)" ng-version="7.0.0"><div style="display:none">import { <a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a>, <a href="api/common/http/HttpHeaders" class="code-anchor">HttpHeaders</a> } from '@angular/common/<a href="api/common/http" class="code-anchor">http</a>';</div><header class="ng-star-inserted">src/app/hero.service.ts (import HTTP symbols)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero.service.ts (import HTTP symbols)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><a href="api/common/http/HttpClient" class="code-anchor"><span class="typ">HttpClient</span></a><span class="pun">,</span><span class="pln"> </span><a href="api/common/http/HttpHeaders" class="code-anchor"><span class="typ">HttpHeaders</span></a><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/common/</span><a href="api/common/http" class="code-anchor"><span class="str">http</span></a><span class="str">'</span><span class="pun">;</span></code>
    </pre></aio-code></code-example><p translation-result="on">把 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 注入到构造函数中一个名叫 <code><a href="api/common/http" class="code-anchor">http</a></code> 的私有属性中。</p><p translation-origin="off">Inject <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> into the constructor in a private property called <code><a href="api/common/http" class="code-anchor">http</a></code>.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="ctor" ng-version="7.0.0"><div style="display:none">constructor( private <a href="api/common/http" class="code-anchor">http</a>: <a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a>, private messageService: MessageService) { }</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">constructor</span><span class="pun">(</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> </span><a href="api/common/http" class="code-anchor"><span class="pln">http</span></a><span class="pun">:</span><span class="pln"> </span><a href="api/common/http/HttpClient" class="code-anchor"><span class="typ">HttpClient</span></a><span class="pun">,</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> messageService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">MessageService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">保留对 <code>MessageService</code> 的注入。你将会频繁调用它，因此请把它包裹进一个私有的 <code><a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor">log</a></code> 方法中。</p><p translation-origin="off">Keep injecting the <code>MessageService</code>. You'll call it so frequently that you'll wrap it in a private <code><a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor">log</a>()</code> method.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="log" ng-version="7.0.0"><div style="display:none">/** Log <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> HeroService <a href="api/common/http/HttpErrorResponse#message" class="code-anchor">message</a> with the MessageService */ private <a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor">log</a>(<a href="api/common/http/HttpErrorResponse#message" class="code-anchor">message</a>: string) { this.messageService.add(`HeroService: ${<a href="api/common/http/HttpErrorResponse#message" class="code-anchor">message</a>}`); }</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="com">/** Log </span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="com">a</span></a><span class="com"> HeroService </span><a href="api/common/http/HttpErrorResponse#message" class="code-anchor"><span class="com">message</span></a><span class="com"> with the MessageService */</span><span class="pln">
</span><span class="kwd">private</span><span class="pln"> </span><a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor"><span class="pln">log</span></a><span class="pun">(</span><a href="api/common/http/HttpErrorResponse#message" class="code-anchor"><span class="pln">message</span></a><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">messageService</span><span class="pun">.</span><span class="kwd">add</span><span class="pun">(</span><span class="str">`HeroService: ${</span><a href="api/common/http/HttpErrorResponse#message" class="code-anchor"><span class="str">message</span></a><span class="str">}`</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">把服务器上英雄数据资源的访问地址 <code>heroesURL</code> 定义为 <code>:base/:collectionName</code> 的形式。 这里的 <code>base</code> 是要请求的资源，而 <code>collectionName</code> 是 <code>in-memory-data-service.ts</code> 中的英雄数据对象。</p><p translation-origin="off">Define the <code>heroesUrl</code> of the form <code>:base/:collectionName</code> with the address of the heroes resource on the server. Here <code>base</code> is the resource to which requests are made, and <code>collectionName</code> is the heroes data object in the <code>in-memory-data-service.ts</code>.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="heroesUrl" ng-version="7.0.0"><div style="display:none">private heroesUrl = 'api/heroes'; // <a href="api/core/SecurityContext#URL" class="code-anchor">URL</a> to web api</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">private</span><span class="pln"> heroesUrl </span><span class="pun">=</span><span class="pln"> </span><span class="str">'api/heroes'</span><span class="pun">;</span><span class="pln">  </span><span class="com">// </span><a href="api/core/SecurityContext#URL" class="code-anchor"><span class="com">URL</span></a><span class="com"> to web api</span></code>
    </pre></aio-code></code-example><h3 id="get-heroes-with-httpclient" translation-result="on">通过 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 获取英雄<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#get-heroes-with-httpclient"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="get-heroes-with-httpclient">Get heroes with <em>HttpClient</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#get-heroes-with-httpclient"><i class="material-icons">link</i></a></h3><p translation-result="on">当前的 <code>HeroService.getHeroes()</code> 使用 RxJS 的 <code>of()</code> 函数来把模拟英雄数据返回为 <code>Observable&lt;Hero[]&gt;</code> 格式。</p><p translation-origin="off">The current <code>HeroService.getHeroes()</code> uses the RxJS <code>of()</code> function to return an array of mock heroes as an <code>Observable&lt;Hero[]&gt;</code>.</p><code-example path="toh-pt4/src/app/hero.service.ts" region="getHeroes-1" header="src/app/hero.service.ts (getHeroes with RxJs 'of()')" ng-version="7.0.0"><div style="display:none">getHeroes(): Observable&lt;Hero[]&gt; { return of(HEROES); }</div><header class="ng-star-inserted">src/app/hero.service.ts (getHeroes with RxJs 'of()')</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero.service.ts (getHeroes with RxJs 'of()')">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">getHeroes</span><span class="pun">():</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> of</span><span class="pun">(</span><span class="pln">HEROES</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">把该方法转换成使用 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 的</p><p translation-origin="off">Convert that method to use <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code></p><code-example path="toh-pt6/src/app/hero.service.ts" region="getHeroes-1" ng-version="7.0.0"><div style="display:none">/** GET heroes from the server */ getHeroes (): Observable&lt;Hero[]&gt; { return this.http.get&lt;Hero[]&gt;(this.heroesUrl) }</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="com">/** GET heroes from the server */</span><span class="pln">
getHeroes </span><span class="pun">():</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroesUrl</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">刷新浏览器后，英雄数据就会从模拟服务器被成功读取。</p><p translation-origin="off">Refresh the browser. The hero data should successfully load from the mock server.</p><p translation-result="on">你用 <code>http.get</code> 替换了 <code>of</code>，没有做其它修改，但是应用仍然在正常工作，这是因为这两个函数都返回了 <code>Observable&lt;Hero[]&gt;</code>。</p><p translation-origin="off">You've swapped <code>of</code> for <code>http.get</code> and the app keeps working without any other changes because both functions return an <code>Observable&lt;Hero[]&gt;</code>.</p><h3 id="http-methods-return-one-value" translation-result="on">Http 方法返回单个值<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#http-methods-return-one-value"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="http-methods-return-one-value">Http methods return one value<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#http-methods-return-one-value"><i class="material-icons">link</i></a></h3><p translation-result="on">所有的 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 方法都会返回某个值的 RxJS <code>Observable</code>。</p><p translation-origin="off">All <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> methods return an RxJS <code>Observable</code> of something.</p><p translation-result="on">HTTP 是一个请求/响应式协议。你发起请求，它返回单个的响应。</p><p translation-origin="off">HTTP is a request/response protocol. You make a request, it returns a single response.</p><p translation-result="on">通常，<code>Observable</code> <em>可以</em>在一段时间内返回多个值。 但来自 <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> 的 <code>Observable</code> 总是发出一个值，然后结束，再也不会发出其它值。</p><p translation-origin="off">In general, an observable <em>can</em> return multiple values over time. An observable from <code><a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a></code> always emits a single value and then completes, never to emit again.</p><p translation-result="on">具体到这次 <code>HttpClient.get</code> 调用，它返回一个 <code>Observable&lt;Hero[]&gt;</code>，顾名思义就是“一个英雄数组的可观察对象”。在实践中，它也只会返回一个英雄数组。</p><p translation-origin="off">This particular <code>HttpClient.get</code> call returns an <code>Observable&lt;Hero[]&gt;</code>, literally "<em>an observable of hero arrays</em>". In practice, it will only return a single hero array.</p><h3 id="httpclientget-returns-response-data" translation-result="on"><code>HttpClient.get</code> 返回响应数据<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#httpclientget-returns-response-data"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="httpclientget-returns-response-data"><em>HttpClient.get</em> returns response data<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#httpclientget-returns-response-data"><i class="material-icons">link</i></a></h3><p translation-result="on"><code>HttpClient.get</code> 默认情况下把响应体当做无类型的 JSON 对象进行返回。 如果指定了可选的模板类型 <code>&lt;Hero[]&gt;</code>，就会给返回你一个类型化的对象。</p><p translation-origin="off"><code>HttpClient.get</code> returns the <em>body</em> of the response as an untyped JSON object by default. Applying the optional type specifier, <code>&lt;Hero[]&gt;</code> , gives you a typed result object.</p><p translation-result="on">JSON 数据的具体形态是由服务器的数据 API 决定的。 <em>英雄指南</em>的数据 API 会把英雄数据作为一个数组进行返回。</p><p translation-origin="off">The shape of the JSON data is determined by the server's data API. The <em>Tour of Heroes</em> data API returns the hero data as an array.</p><div class="alert is-helpful"><p translation-result="on">其它 API 可能在返回对象中深埋着你想要的数据。 你可能要借助 RxJS 的 <code>map</code> 操作符对 <code>Observable</code> 的结果进行处理，以便把这些数据挖掘出来。</p><p translation-origin="off">Other APIs may bury the data that you want within an object. You might have to dig that data out by processing the <code>Observable</code> result with the RxJS <code>map</code> operator.</p><p translation-result="on">虽然不打算在此展开讨论，不过你可以到范例源码中的 <code>getHeroNo404()</code> 方法中找到一个使用 <code>map</code> 操作符的例子。</p><p translation-origin="off">Although not discussed here, there's an example of <code>map</code> in the <code>getHeroNo404()</code> method included in the sample source code.</p></div><h3 id="error-handling" translation-result="on">错误处理<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#error-handling"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="error-handling">Error handling<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#error-handling"><i class="material-icons">link</i></a></h3><p translation-result="on">凡事皆会出错，特别是当你从远端服务器获取数据的时候。 <code>HeroService.getHeroes()</code> 方法应该捕获错误，并做适当的处理。</p><p translation-origin="off">Things go wrong, especially when you're getting data from a remote server. The <code>HeroService.getHeroes()</code> method should catch errors and do something appropriate.</p><p translation-result="on">要捕获错误，你就要使用 RxJS 的 <code>catchError()</code> 操作符来<strong>建立对 Observable 结果的处理管道（pipe）</strong>。</p><p translation-origin="off">To catch errors, you <strong>"pipe" the observable</strong> result from <code>http.get()</code> through an RxJS <code>catchError()</code> operator.</p><p translation-result="on">从 <code>rxjs/operators</code> 中导入 <code>catchError</code> 符号，以及你稍后将会用到的其它操作符。</p><p translation-origin="off">Import the <code>catchError</code> symbol from <code>rxjs/operators</code>, along with some other operators you'll need later.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="import-rxjs-operators" ng-version="7.0.0"><div style="display:none">import { catchError, map, tap } from 'rxjs/operators';</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> catchError</span><span class="pun">,</span><span class="pln"> map</span><span class="pun">,</span><span class="pln"> tap </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'rxjs/operators'</span><span class="pun">;</span></code>
    </pre></aio-code></code-example><p translation-result="on">现在，使用 <code>.pipe()</code> 方法来扩展 <code>Observable</code> 的结果，并给它一个 <code>catchError()</code> 操作符。</p><p translation-origin="off">Now extend the observable result with the <code>.pipe()</code> method and give it a <code>catchError()</code> operator.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="getHeroes-2" ng-version="7.0.0"><div style="display:none">getHeroes (): Observable&lt;Hero[]&gt; { return this.http.get&lt;Hero[]&gt;(this.heroesUrl) .pipe( catchError(this.handleError&lt;Hero[]&gt;('getHeroes', [])) ); }</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">getHeroes </span><span class="pun">():</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroesUrl</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
      catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="str">'getHeroes'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[]))</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on"><code>catchError()</code> 操作符会拦截<strong>失败的 <code>Observable</code></strong>。 它把错误对象传给<em>错误处理器</em>，<em>错误处理器</em>会处理这个错误。</p><p translation-origin="off">The <code>catchError()</code> operator intercepts an <strong><code>Observable</code> that failed</strong>. It passes the error an <em>error handler</em> that can do what it wants with the error.</p><p translation-result="on">下面的 <code><a href="api/core/ErrorHandler#handleError" class="code-anchor">handleError()</a></code> 方法会报告这个错误，并返回一个无害的结果（安全值），以便应用能正常工作。</p><p translation-origin="off">The following <code><a href="api/core/ErrorHandler#handleError" class="code-anchor">handleError()</a></code> method reports the error and then returns an innocuous result so that the application keeps working.</p><h4 id="handleerror"><em>handleError</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#handleerror"><i class="material-icons">link</i></a></h4><p translation-result="on">下面这个 <code><a href="api/core/ErrorHandler#handleError" class="code-anchor">handleError()</a></code> 将会在很多 <code>HeroService</code> 的方法之间共享，所以要把它通用化，以支持这些彼此不同的需求。</p><p translation-origin="off">The following <code><a href="api/core/ErrorHandler#handleError" class="code-anchor">handleError()</a></code> will be shared by many <code>HeroService</code> methods so it's generalized to meet their different needs.</p><p translation-result="on">它不再直接处理这些错误，而是返回给 <code>catchError</code> 返回一个<em>错误处理</em>函数。还要用操作名和出错时要返回的安全值来对这个错误处理函数进行配置。</p><p translation-origin="off">Instead of handling the error directly, it returns an <em>error handler</em> function to <code>catchError</code> that it has configured with both the name of the operation that failed and a safe return value.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="handleError" ng-version="7.0.0"><div style="display:none">/** * Handle <a href="api/http/Http" class="code-anchor">Http</a> operation that failed. * Let the app continue. * @param operation - name of the operation that failed * @param result - <a href="api/animations/AnimationQueryOptions#optional" class="code-anchor">optional</a> value to return as the observable result */ private handleError&lt;T&gt; (operation = 'operation', result?: T) { return (error: any): Observable&lt;T&gt; =&gt; { // TODO: send the error to remote logging infrastructure console.error(error); // <a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor">log</a> to console instead // TODO: better job of transforming error for user consumption this.log(`${operation} failed: ${error.message}`); // Let the app keep running by returning an empty result. return of(result as T); }; }</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><ol class="linenums"><li class="L0"><span class="com">/**</span></li><li class="L1"><span class="com"> * Handle </span><a href="api/http/Http" class="code-anchor"><span class="com">Http</span></a><span class="com"> operation that failed.</span></li><li class="L2"><span class="com"> * Let the app continue.</span></li><li class="L3"><span class="com"> * @param operation - name of the operation that failed</span></li><li class="L4"><span class="com"> * @param result - </span><a href="api/animations/AnimationQueryOptions#optional" class="code-anchor"><span class="com">optional</span></a><span class="com"> value to return as the observable result</span></li><li class="L5"><span class="com"> */</span></li><li class="L6"><span class="kwd">private</span><span class="pln"> handleError</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">operation </span><span class="pun">=</span><span class="pln"> </span><span class="str">'operation'</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">?:</span><span class="pln"> T</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></li><li class="L7"><span class="pln">  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L8"><span class="pln">&nbsp;</span></li><li class="L9"><span class="pln">    </span><span class="com">// TODO: send the error to remote logging infrastructure</span></li><li class="L0"><span class="pln">    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">error</span><span class="pun">);</span><span class="pln"> </span><span class="com">// </span><a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor"><span class="com">log</span></a><span class="com"> to console instead</span></li><li class="L1"><span class="pln">&nbsp;</span></li><li class="L2"><span class="pln">    </span><span class="com">// TODO: better job of transforming error for user consumption</span></li><li class="L3"><span class="pln">    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`${operation} failed: ${error.message}`</span><span class="pun">);</span></li><li class="L4"><span class="pln">&nbsp;</span></li><li class="L5"><span class="pln">    </span><span class="com">// Let the app keep running by returning an empty result.</span></li><li class="L6"><span class="pln">    </span><span class="kwd">return</span><span class="pln"> of</span><span class="pun">(</span><span class="pln">result </span><span class="kwd">as</span><span class="pln"> T</span><span class="pun">);</span></li><li class="L7"><span class="pln">  </span><span class="pun">};</span></li><li class="L8"><span class="pun">}</span></li></ol></code>
    </pre></aio-code></code-example><p translation-result="on">在控制台中汇报了这个错误之后，这个处理器会汇报一个用户友好的消息，并给应用返回一个安全值，让它继续工作。</p><p translation-origin="off">After reporting the error to console, the handler constructs a user friendly message and returns a safe value to the app so it can keep working.</p><p translation-result="on">因为每个服务方法都会返回不同类型的 <code>Observable</code> 结果，因此 <code><a href="api/core/ErrorHandler#handleError" class="code-anchor">handleError()</a></code> 也需要一个类型参数，以便它返回一个此类型的安全值，正如应用所期望的那样。</p><p translation-origin="off">Because each service method returns a different kind of <code>Observable</code> result, <code><a href="api/core/ErrorHandler#handleError" class="code-anchor">handleError()</a></code> takes a type parameter so it can return the safe value as the type that the app expects.</p><h3 id="tap-into-the-observable" translation-result="on">窥探 <code>Observable</code><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#tap-into-the-observable"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="tap-into-the-observable">Tap into the <em>Observable</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#tap-into-the-observable"><i class="material-icons">link</i></a></h3><p translation-result="on"><code>HeroService</code> 的方法将会窥探 <code>Observable</code> 的数据流，并通过 <code><a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor">log</a>()</code> 函数往页面底部发送一条消息。</p><p translation-origin="off">The <code>HeroService</code> methods will <strong>tap</strong> into the flow of observable values and send a message (via <code><a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor">log</a>()</code>) to the message area at the bottom of the page.</p><p translation-result="on">它们可以使用 RxJS 的 <code>tap</code> 操作符来实现，该操作符会<em>查看</em> Observable 中的值，使用那些值做一些事情，并且把它们传出来。 这种 <code>tap</code> 回调不会改变这些值本身。</p><p translation-origin="off">They'll do that with the RxJS <code>tap</code> operator, which <em>looks</em> at the observable values, does <em>something</em> with those values, and passes them along. The <code>tap</code> call back doesn't touch the values themselves.</p><p translation-result="on">下面是 <code>getHeroes</code> 的最终版本，它使用 <code>tap</code> 来记录各种操作。</p><p translation-origin="off">Here is the final version of <code>getHeroes</code> with the <code>tap</code> that logs the operation.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="getHeroes" ng-version="7.0.0"><div style="display:none">/** GET heroes from the server */ getHeroes (): Observable&lt;Hero[]&gt; { return this.http.get&lt;Hero[]&gt;(this.heroesUrl) .pipe( tap(_ =&gt; this.log('fetched heroes')), catchError(this.handleError&lt;Hero[]&gt;('getHeroes', [])) ); }</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="com">/** GET heroes from the server */</span><span class="pln">
getHeroes </span><span class="pun">():</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroesUrl</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
      tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'fetched heroes'</span><span class="pun">)),</span><span class="pln">
      catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="str">'getHeroes'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[]))</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><h3 id="get-hero-by-id" translation-result="on">通过 id 获取英雄<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#get-hero-by-id"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="get-hero-by-id">Get hero by id<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#get-hero-by-id"><i class="material-icons">link</i></a></h3><p translation-result="on">大多数的 Web API 都支持以 <code>:baseURL/:id</code> 的形式根据 id 进行获取。</p><p translation-origin="off">Most web APIs support a <em>get by id</em> request in the form <code>:baseURL/:id</code>.</p><p translation-result="on">这里的 <code>baseURL</code> 就是在 <a href="http://localhost:4800/tutorial/toh-pt6#heroes-and-http">英雄列表与 HTTP</a> 部分定义过的 <code>heroesURL</code>（<code>api/heroes</code>）。而 <code>id</code> 则是你要获取的英雄的编号，比如，<code>api/heroes/11</code>。 添加一个 <code>HeroService.getHero()</code> 方法，以发起该请求：</p><p translation-origin="off">Here, the <em>base URL</em> is the <code>heroesURL</code> defined in the <a href="tutorial/toh-pt6#heroes-and-http">Heroes and HTTP</a> section (<code>api/heroes</code>) and <em>id</em> is the number of the hero that you want to retrieve. For example, <code>api/heroes/11</code>. Add a <code>HeroService.getHero()</code> method to make that request:</p><code-example path="toh-pt6/src/app/hero.service.ts" region="getHero" header="src/app/hero.service.ts" ng-version="7.0.0"><div style="display:none">/** GET hero by id. Will 404 if id not found */ getHero(id: number): Observable&lt;Hero&gt; { const url = `${this.heroesUrl}/${id}`; return this.http.get&lt;Hero&gt;(url).pipe( tap(_ =&gt; this.log(`fetched hero id=${id}`)), catchError(this.handleError&lt;Hero&gt;(`getHero id=${id}`)) ); }</div><header class="ng-star-inserted">src/app/hero.service.ts</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero.service.ts">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="com">/** GET hero by id. Will 404 if id not found */</span><span class="pln">
getHero</span><span class="pun">(</span><span class="pln">id</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">`${this.heroesUrl}/${id}`</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="pln">url</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
    tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`fetched hero id=${id}`</span><span class="pun">)),</span><span class="pln">
    catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="str">`getHero id=${id}`</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">这里和 <code>getHeroes()</code> 相比有三个显著的差异。</p><p translation-origin="off">There are three significant differences from <code>getHeroes()</code>.</p><ul><li><p translation-result="on">它使用想获取的英雄的 id 构建了一个请求 URL。</p><p translation-origin="off">it constructs a request URL with the desired hero's id.</p></li><li><p translation-result="on">服务器应该使用单个英雄作为回应，而不是一个英雄数组。</p><p translation-origin="off">the server should respond with a single hero rather than an array of heroes.</p></li><li><p translation-result="on">所以，<code>getHero</code> 会返回 <code>Observable&lt;Hero&gt;</code>（“一个可观察的<em>单个英雄对象</em>”），而不是一个可观察的英雄对象<em>数组</em>。</p><p translation-origin="off">therefore, <code>getHero</code> returns an <code>Observable&lt;Hero&gt;</code> ("<em>an observable of Hero objects</em>") rather than an observable of hero <em>arrays</em> .</p></li></ul><h2 id="update-heroes" translation-result="on">修改英雄<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#update-heroes"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="update-heroes">Update heroes<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#update-heroes"><i class="material-icons">link</i></a></h2><p translation-result="on">在<em>英雄详情</em>视图中编辑英雄的名字。 随着输入，英雄的名字也跟着在页面顶部的标题区更新了。 但是当你点击“后退”按钮时，这些修改都丢失了。</p><p translation-origin="off">Edit a hero's name in the <em>hero detail</em> view. As you type, the hero name updates the heading at the top of the page. But when you click the "go back button", the changes are lost.</p><p translation-result="on">如果你希望保留这些修改，就要把它们写回到服务器。</p><p translation-origin="off">If you want changes to persist, you must write them back to the server.</p><p translation-result="on">在英雄详情模板的底部添加一个保存按钮，它绑定了一个 <code>click</code> 事件，事件绑定会调用组件中一个名叫 <code>save()</code> 的新方法：</p><p translation-origin="off">At the end of the hero detail template, add a save button with a <code>click</code> event binding that invokes a new component method named <code>save()</code>.</p><code-example path="toh-pt6/src/app/hero-detail/hero-detail.component.html" region="save" header="src/app/hero-detail/hero-detail.component.html (save)" ng-version="7.0.0"><div style="display:none">&lt;button (click)="save()"&gt;save&lt;/button&gt;</div><header class="ng-star-inserted">src/app/hero-detail/hero-detail.component.html (save)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero-detail/hero-detail.component.html (save)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"save()"</span><span class="tag">&gt;</span><span class="pln">save</span><span class="tag">&lt;/button&gt;</span></code>
    </pre></aio-code></code-example><p translation-result="on">添加如下的 <code>save()</code> 方法，它使用英雄服务中的 <code>updateHero()</code> 方法来保存对英雄名字的修改，然后导航回前一个视图。</p><p translation-origin="off">Add the following <code>save()</code> method, which persists hero name changes using the hero service <code>updateHero()</code> method and then navigates back to the previous view.</p><code-example path="toh-pt6/src/app/hero-detail/hero-detail.component.ts" region="save" header="src/app/hero-detail/hero-detail.component.ts (save)" ng-version="7.0.0"><div style="display:none">save(): void { this.heroService.updateHero(this.hero) .subscribe(() =&gt; this.goBack()); }</div><header class="ng-star-inserted">src/app/hero-detail/hero-detail.component.ts (save)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero-detail/hero-detail.component.ts (save)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">save</span><span class="pun">():</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroService</span><span class="pun">.</span><span class="pln">updateHero</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">hero</span><span class="pun">)</span><span class="pln">
     </span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">goBack</span><span class="pun">());</span><span class="pln">
 </span><span class="pun">}</span></code>
    </pre></aio-code></code-example><h4 id="add-heroserviceupdatehero" translation-result="on">添加 <em>HeroService.updateHero()</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-heroserviceupdatehero"><i class="material-icons">link</i></a></h4><h4 translation-origin="off" id="add-heroserviceupdatehero">Add <em>HeroService.updateHero()</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-heroserviceupdatehero"><i class="material-icons">link</i></a></h4><p translation-result="on"><code>updateHero()</code> 的总体结构和 <code>getHeroes()</code> 很相似，但它会使用 <code>http.put()</code> 来把修改后的英雄保存到服务器上。</p><p translation-origin="off">The overall structure of the <code>updateHero()</code> method is similar to that of <code>getHeroes()</code>, but it uses <code>http.put()</code> to persist the changed hero on the server.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="updateHero" header="src/app/hero.service.ts (update)" ng-version="7.0.0"><div style="display:none">/** PUT: <a href="api/forms/NgModel#update" class="code-anchor">update</a> the hero on the server */ updateHero (hero: Hero): Observable&lt;any&gt; { return this.http.put(this.heroesUrl, hero, httpOptions).pipe( tap(_ =&gt; this.log(`updated hero id=${hero.id}`)), catchError(this.handleError&lt;any&gt;('updateHero')) ); }</div><header class="ng-star-inserted">src/app/hero.service.ts (update)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero.service.ts (update)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="com">/** PUT: </span><a href="api/forms/NgModel#update" class="code-anchor"><span class="com">update</span></a><span class="com"> the hero on the server */</span><span class="pln">
updateHero </span><span class="pun">(</span><span class="pln">hero</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="str">&lt;any&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroesUrl</span><span class="pun">,</span><span class="pln"> hero</span><span class="pun">,</span><span class="pln"> httpOptions</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
    tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`updated hero id=${hero.id}`</span><span class="pun">)),</span><span class="pln">
    catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="str">&lt;any&gt;</span><span class="pun">(</span><span class="str">'updateHero'</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on"><code><a href="api/common/http/HttpClient#put" class="code-anchor">HttpClient.put()</a></code> 方法接受三个参数</p><p translation-origin="off">The <code><a href="api/common/http/HttpClient#put" class="code-anchor">HttpClient.put()</a></code> method takes three parameters</p><ul><li><p translation-result="on">URL 地址</p><p translation-origin="off">the URL</p></li><li><p translation-result="on">要修改的数据（这里就是修改后的英雄）</p><p translation-origin="off">the data to update (the modified hero in this case)</p></li><li><p translation-result="on">选项</p><p translation-origin="off">options</p></li></ul><p translation-result="on">URL 没变。英雄 Web API 通过英雄对象的 <code>id</code> 就可以知道要修改哪个英雄。</p><p translation-origin="off">The URL is unchanged. The heroes web API knows which hero to update by looking at the hero's <code>id</code>.</p><p translation-result="on">英雄 Web API 期待在保存时的请求中有一个特殊的头。 这个头是在 <code>HeroService</code> 的 <code>httpOptions</code> 常量中定义的。</p><p translation-origin="off">The heroes web API expects a special header in HTTP save requests. That header is in the <code>httpOptions</code> constant defined in the <code>HeroService</code>.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="http-options" header="src/app/hero.service.ts" ng-version="7.0.0"><div style="display:none">const httpOptions = { headers: new <a href="api/common/http/HttpHeaders" class="code-anchor">HttpHeaders</a>({ 'Content-Type': 'application/json' }) };</div><header class="ng-star-inserted">src/app/hero.service.ts</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero.service.ts">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">const</span><span class="pln"> httpOptions </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><a href="api/common/http/HttpHeaders" class="code-anchor"><span class="typ">HttpHeaders</span></a><span class="pun">({</span><span class="pln"> </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pln"> </span><span class="pun">})</span><span class="pln">
</span><span class="pun">};</span></code>
    </pre></aio-code></code-example><p translation-result="on">刷新浏览器，修改英雄名，保存这些修改。在 <code>HeroDetailComponent</code> 的 <code>save()</code> 方法中实现了 "导航到前一个视图" 的逻辑。 现在，改名后的英雄已经显示在列表中了。</p><p translation-origin="off">Refresh the browser, change a hero name and save your change. Navigating to the previous view is implemented in the <code>save()</code> method defined in <code>HeroDetailComponent</code>. The hero now appears in the list with the changed name.</p><h2 id="add-a-new-hero" translation-result="on">添加新英雄<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-a-new-hero"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="add-a-new-hero">Add a new hero<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-a-new-hero"><i class="material-icons">link</i></a></h2><p translation-result="on">要添加英雄，本应用中只需要英雄的名字。你可以使用一个和添加按钮成对的 <code>input</code> 元素。</p><p translation-origin="off">To add a hero, this app only needs the hero's name. You can use an <code>input</code> element paired with an add button.</p><p translation-result="on">把下列代码插入到 <code>HeroesComponent</code> 模板中标题的紧后面：</p><p translation-origin="off">Insert the following into the <code>HeroesComponent</code> template, just after the heading:</p><code-example path="toh-pt6/src/app/heroes/heroes.component.html" region="add" header="src/app/heroes/heroes.component.html (add)" ng-version="7.0.0"><div style="display:none">&lt;div&gt; &lt;label&gt;Hero name: &lt;input #heroName /&gt; &lt;/label&gt; &lt;!-- (click) passes input value to add() and then clears the input --&gt; &lt;button (click)="add(heroName.value); heroName.value=''"&gt; add &lt;/button&gt; &lt;/div&gt;</div><header class="ng-star-inserted">src/app/heroes/heroes.component.html (add)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/heroes/heroes.component.html (add)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="tag">&lt;div&gt;</span><span class="pln">
  </span><span class="tag">&lt;label&gt;</span><span class="pln">Hero name:
    </span><span class="tag">&lt;input</span><span class="pln"> #</span><span class="atn">heroName</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/label&gt;</span><span class="pln">
  </span><span class="com">&lt;!-- (click) passes input value to add() and then clears the input --&gt;</span><span class="pln">
  </span><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"add(heroName.value); heroName.value=''"</span><span class="tag">&gt;</span><span class="pln">
    add
  </span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></code>
    </pre></aio-code></code-example><p translation-result="on">当点击事件触发时，调用组件的点击处理器，然后清空这个输入框，以便用来输入另一个名字。</p><p translation-origin="off">In response to a click event, call the component's click handler and then clear the input field so that it's ready for another name.</p><code-example path="toh-pt6/src/app/heroes/heroes.component.ts" region="add" header="src/app/heroes/heroes.component.ts (add)" ng-version="7.0.0"><div style="display:none">add(name: string): void { name = name.trim(); if (!name) { return; } this.heroService.addHero({ name } as Hero) .subscribe(hero =&gt; { this.heroes.push(hero); }); }</div><header class="ng-star-inserted">src/app/heroes/heroes.component.ts (add)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/heroes/heroes.component.ts (add)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">add</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">):</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">();</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroService</span><span class="pun">.</span><span class="pln">addHero</span><span class="pun">({</span><span class="pln"> name </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> </span><span class="typ">Hero</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">hero </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroes</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">hero</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">当指定的名字非空时，这个处理器会用这个名字创建一个类似于 <code>Hero</code> 的对象（只缺少 <code>id</code> 属性），并把它传给服务的 <code>addHero()</code> 方法。</p><p translation-origin="off">When the given name is non-blank, the handler creates a <code>Hero</code>-like object from the name (it's only missing the <code>id</code>) and passes it to the services <code>addHero()</code> method.</p><p translation-result="on">当 <code>addHero</code> 保存成功时，<code>subscribe</code> 的回调函数会收到这个新英雄，并把它追加到 <code>heroes</code> 列表中以供显示。</p><p translation-origin="off">When <code>addHero</code> saves successfully, the <code>subscribe</code> callback receives the new hero and pushes it into to the <code>heroes</code> list for display.</p><p translation-result="on">你将在下一节编写 <code>HeroService.addHero</code>。</p><p translation-origin="off">You'll write <code>HeroService.addHero</code> in the next section.</p><h4 id="add-heroserviceaddhero" translation-result="on">添加 <code>HeroService.addHero()</code><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-heroserviceaddhero"><i class="material-icons">link</i></a></h4><h4 translation-origin="off" id="add-heroserviceaddhero">Add <em>HeroService.addHero()</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-heroserviceaddhero"><i class="material-icons">link</i></a></h4><p translation-result="on">往 <code>HeroService</code> 类中添加 <code>addHero()</code> 方法。</p><p translation-origin="off">Add the following <code>addHero()</code> method to the <code>HeroService</code> class.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="addHero" header="src/app/hero.service.ts (addHero)" ng-version="7.0.0"><div style="display:none">/** POST: add <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> new hero to the server */ addHero (hero: Hero): Observable&lt;Hero&gt; { return this.http.post&lt;Hero&gt;(this.heroesUrl, hero, httpOptions).pipe( tap((newHero: Hero) =&gt; this.log(`added hero w/ id=${newHero.id}`)), catchError(this.handleError&lt;Hero&gt;('addHero')) ); }</div><header class="ng-star-inserted">src/app/hero.service.ts (addHero)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero.service.ts (addHero)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="com">/** POST: add </span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="com">a</span></a><span class="com"> new hero to the server */</span><span class="pln">
addHero </span><span class="pun">(</span><span class="pln">hero</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="pln">post</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroesUrl</span><span class="pun">,</span><span class="pln"> hero</span><span class="pun">,</span><span class="pln"> httpOptions</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
    tap</span><span class="pun">((</span><span class="pln">newHero</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`added hero w/ id=${newHero.id}`</span><span class="pun">)),</span><span class="pln">
    catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="str">'addHero'</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on"><code>HeroService.addHero()</code> 和 <code>updateHero</code> 有两点不同。</p><p translation-origin="off"><code>HeroService.addHero()</code> differs from <code>updateHero</code> in two ways.</p><ul><li><p translation-result="on">它调用 <code><a href="api/common/http/HttpClient#post" class="code-anchor">HttpClient.post()</a></code> 而不是 <code>put()</code>。</p><p translation-origin="off">it calls <code><a href="api/common/http/HttpClient#post" class="code-anchor">HttpClient.post()</a></code> instead of <code>put()</code>.</p></li><li><p translation-result="on">它期待服务器为这个新的英雄生成一个 id，然后把它通过 <code>Observable&lt;Hero&gt;</code> 返回给调用者。</p><p translation-origin="off">it expects the server to generate an id for the new hero, which it returns in the <code>Observable&lt;Hero&gt;</code> to the caller.</p></li></ul><p translation-result="on">刷新浏览器，并添加一些英雄。</p><p translation-origin="off">Refresh the browser and add some heroes.</p><h2 id="delete-a-hero" translation-result="on">删除某个英雄<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#delete-a-hero"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="delete-a-hero">Delete a hero<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#delete-a-hero"><i class="material-icons">link</i></a></h2><p translation-result="on">英雄列表中的每个英雄都有一个删除按钮。</p><p translation-origin="off">Each hero in the heroes list should have a delete button.</p><p translation-result="on">把下列按钮（<code>button</code>）元素添加到 <code>HeroesComponent</code> 的模板中，就在每个 <code>&lt;li&gt;</code> 元素中的英雄名字后方。</p><p translation-origin="off">Add the following button element to the <code>HeroesComponent</code> template, after the hero name in the repeated <code>&lt;li&gt;</code> element.</p><code-example path="toh-pt6/src/app/heroes/heroes.component.html" region="delete" ng-version="7.0.0"><div style="display:none">&lt;button class="delete" title="delete hero" (click)="delete(hero)"&gt;x&lt;/button&gt;</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"delete"</span><span class="pln"> </span><span class="atn">title</span><span class="pun">=</span><span class="atv">"delete hero"</span><span class="pln">
  (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"delete(hero)"</span><span class="tag">&gt;</span><span class="pln">x</span><span class="tag">&lt;/button&gt;</span></code>
    </pre></aio-code></code-example><p translation-result="on">英雄列表的 HTML 应该是这样的：</p><p translation-origin="off">The HTML for the list of heroes should look like this:</p><code-example path="toh-pt6/src/app/heroes/heroes.component.html" region="list" header="src/app/heroes/heroes.component.html (list of heroes)" ng-version="7.0.0"><div style="display:none">&lt;ul class="heroes"&gt; &lt;li *<a href="api/common/NgForOf" class="code-anchor">ngFor</a>="let hero of heroes"&gt; &lt;<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> <a href="api/router/RouterLink" class="code-anchor">routerLink</a>="/detail/{{hero.id}}"&gt; &lt;span class="badge"&gt;{{hero.id}}&lt;/span&gt; {{hero.name}} &lt;/<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a>&gt; &lt;button class="delete" title="delete hero" (click)="delete(hero)"&gt;x&lt;/button&gt; &lt;/li&gt; &lt;/ul&gt;</div><header class="ng-star-inserted">src/app/heroes/heroes.component.html (list of heroes)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/heroes/heroes.component.html (list of heroes)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="tag">&lt;ul</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"heroes"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;li</span><span class="pln"> *</span><a href="api/common/NgForOf" class="code-anchor"><span class="atn">ngFor</span></a><span class="pun">=</span><span class="atv">"let hero of heroes"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="pln"> </span><a href="api/router/RouterLink" class="code-anchor"><span class="atn">routerLink</span></a><span class="pun">=</span><span class="atv">"/detail/{{hero.id}}"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"badge"</span><span class="tag">&gt;</span><span class="pln">{{hero.id}}</span><span class="tag">&lt;/span&gt;</span><span class="pln"> {{hero.name}}
    </span><span class="tag">&lt;/</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"delete"</span><span class="pln"> </span><span class="atn">title</span><span class="pun">=</span><span class="atv">"delete hero"</span><span class="pln">
      (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"delete(hero)"</span><span class="tag">&gt;</span><span class="pln">x</span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;/li&gt;</span><span class="pln">
</span><span class="tag">&lt;/ul&gt;</span></code>
    </pre></aio-code></code-example><p translation-result="on">要把删除按钮定位在每个英雄条目的最右边，就要往 <code>heroes.component.css</code> 中添加一些 CSS。你可以在下方的 <a href="tutorial/toh-pt6#heroescomponent">最终代码</a> 中找到这些 CSS。</p><p translation-origin="off">To position the delete button at the far right of the hero entry, add some CSS to the <code>heroes.component.css</code>. You'll find that CSS in the <a href="tutorial/toh-pt6#heroescomponent">final review code</a> below.</p><p translation-result="on">把 <code>delete()</code> 处理器添加到组件中。</p><p translation-origin="off">Add the <code>delete()</code> handler to the component.</p><code-example path="toh-pt6/src/app/heroes/heroes.component.ts" region="delete" header="src/app/heroes/heroes.component.ts (delete)" ng-version="7.0.0"><div style="display:none">delete(hero: Hero): void { this.heroes = this.heroes.filter(h =&gt; h !== hero); this.heroService.deleteHero(hero).subscribe(); }</div><header class="ng-star-inserted">src/app/heroes/heroes.component.ts (delete)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/heroes/heroes.component.ts (delete)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">delete</span><span class="pun">(</span><span class="pln">hero</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pun">):</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroes </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroes</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">h </span><span class="pun">=&gt;</span><span class="pln"> h </span><span class="pun">!==</span><span class="pln"> hero</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroService</span><span class="pun">.</span><span class="pln">deleteHero</span><span class="pun">(</span><span class="pln">hero</span><span class="pun">).</span><span class="pln">subscribe</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">虽然这个组件把删除英雄的逻辑委托给了 <code>HeroService</code>，但扔保留了更新它自己的英雄列表的职责。 组件的 <code>delete()</code> 方法会在 <code>HeroService</code> 对服务器的操作成功之前，先从列表中移除<em>要删除的英雄</em>。</p><p translation-origin="off">Although the component delegates hero deletion to the <code>HeroService</code>, it remains responsible for updating its own list of heroes. The component's <code>delete()</code> method immediately removes the <em>hero-to-delete</em> from that list, anticipating that the <code>HeroService</code> will succeed on the server.</p><p translation-result="on">组件与 <code>heroService.delete()</code> 返回的 <code>Observable</code> 还完全没有关联。<strong>必须订阅它</strong>。</p><p translation-origin="off">There's really nothing for the component to do with the <code>Observable</code> returned by <code>heroService.delete()</code>. <strong>It must subscribe anyway</strong>.</p><div class="alert is-important"><p translation-result="on">如果你忘了调用 <code>subscribe()</code>，本服务将不会把这个删除请求发送给服务器。 作为一条通用的规则，<code>Observable</code> 在有人订阅之前<em>什么都不会做</em>。</p><p translation-origin="off">If you neglect to <code>subscribe()</code>, the service will not send the delete request to the server! As a rule, an <code>Observable</code> <em>does nothing</em> until something subscribes!</p><p translation-result="on">你可以暂时删除 <code>subscribe()</code> 来确认这一点。点击“Dashboard”，然后点击“Heroes”，就又看到完整的英雄列表了。</p><p translation-origin="off">Confirm this for yourself by temporarily removing the <code>subscribe()</code>, clicking "Dashboard", then clicking "Heroes". You'll see the full list of heroes again.</p></div><h4 id="add-heroservicedeletehero" translation-result="on">添加 <code>HeroService.deleteHero()</code><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-heroservicedeletehero"><i class="material-icons">link</i></a></h4><h4 translation-origin="off" id="add-heroservicedeletehero">Add <em>HeroService.deleteHero()</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-heroservicedeletehero"><i class="material-icons">link</i></a></h4><p translation-result="on">把 <code>deleteHero()</code> 方法添加到 <code>HeroService</code> 中，代码如下。</p><p translation-origin="off">Add a <code>deleteHero()</code> method to <code>HeroService</code> like this.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="deleteHero" header="src/app/hero.service.ts (delete)" ng-version="7.0.0"><div style="display:none">/** DELETE: delete the hero from the server */ deleteHero (hero: Hero | number): Observable&lt;Hero&gt; { const id = typeof hero === 'number' ? hero : hero.id; const url = `${this.heroesUrl}/${id}`; return this.http.delete&lt;Hero&gt;(url, httpOptions).pipe( tap(_ =&gt; this.log(`deleted hero id=${id}`)), catchError(this.handleError&lt;Hero&gt;('deleteHero')) ); }</div><header class="ng-star-inserted">src/app/hero.service.ts (delete)</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero.service.ts (delete)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="com">/** DELETE: delete the hero from the server */</span><span class="pln">
deleteHero </span><span class="pun">(</span><span class="pln">hero</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">typeof</span><span class="pln"> hero </span><span class="pun">===</span><span class="pln"> </span><span class="str">'number'</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> hero </span><span class="pun">:</span><span class="pln"> hero</span><span class="pun">.</span><span class="pln">id</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">`${this.heroesUrl}/${id}`</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> httpOptions</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
    tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`deleted hero id=${id}`</span><span class="pun">)),</span><span class="pln">
    catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="str">'deleteHero'</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">注意</p><p translation-origin="off">Note that</p><ul><li><p translation-result="on">它调用了 <code>HttpClient.delete</code>。</p><p translation-origin="off">it calls <code>HttpClient.delete</code>.</p></li><li><p translation-result="on">URL 就是英雄的资源 URL 加上要删除的英雄的 <code>id</code>。</p><p translation-origin="off">the URL is the heroes resource URL plus the <code>id</code> of the hero to delete</p></li><li><p translation-result="on">你不用像 <code>put</code> 和 <code>post</code> 中那样发送任何数据。</p><p translation-origin="off">you don't send data as you did with <code>put</code> and <code>post</code>.</p></li><li><p translation-result="on">你仍要发送 <code>httpOptions</code>。</p><p translation-origin="off">you still send the <code>httpOptions</code>.</p></li></ul><p translation-result="on">刷新浏览器，并试一下这个新的删除功能。</p><p translation-origin="off">Refresh the browser and try the new delete functionality.</p><h2 id="search-by-name" translation-result="on">根据名字搜索<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#search-by-name"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="search-by-name">Search by name<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#search-by-name"><i class="material-icons">link</i></a></h2><p translation-result="on">在最后一次练习中，你要学到把 <code>Observable</code> 的操作符串在一起，让你能将相似 HTTP 请求的数量最小化，并节省网络带宽。</p><p translation-origin="off">In this last exercise, you learn to chain <code>Observable</code> operators together so you can minimize the number of similar HTTP requests and consume network bandwidth economically.</p><p translation-result="on">你将往<em>仪表盘</em>中加入<em>英雄搜索</em>特性。 当用户在搜索框中输入名字时，你会不断发送根据名字过滤英雄的 HTTP 请求。 你的目标是仅仅发出尽可能少的必要请求。</p><p translation-origin="off">You will add a <em>heroes search</em> feature to the <em>Dashboard</em>. As the user types a name into a search box, you'll make repeated HTTP requests for heroes filtered by that name. Your goal is to issue only as many requests as necessary.</p><h4 id="heroservicesearchheroes"><em>HeroService.searchHeroes</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#heroservicesearchheroes"><i class="material-icons">link</i></a></h4><p translation-result="on">先把 <code>searchHeroes</code> 方法添加到 <code>HeroService</code> 中。</p><p translation-origin="off">Start by adding a <code>searchHeroes</code> method to the <code>HeroService</code>.</p><code-example path="toh-pt6/src/app/hero.service.ts" region="searchHeroes" header="src/app/hero.service.ts" ng-version="7.0.0"><div style="display:none">/* GET heroes whose name contains <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> term */ searchHeroes(term: string): Observable&lt;Hero[]&gt; { if (!term.trim()) { // if not <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> term, return empty hero array. return of([]); } return this.http.get&lt;Hero[]&gt;(`${this.heroesUrl}/?name=${term}`).pipe( tap(_ =&gt; this.log(`found heroes matching "${term}"`)), catchError(this.handleError&lt;Hero[]&gt;('searchHeroes', [])) ); }</div><header class="ng-star-inserted">src/app/hero.service.ts</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero.service.ts">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="com">/* GET heroes whose name contains </span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="com">search</span></a><span class="com"> term */</span><span class="pln">
searchHeroes</span><span class="pun">(</span><span class="pln">term</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">term</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// if not </span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="com">search</span></a><span class="com"> term, return empty hero array.</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> of</span><span class="pun">([]);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="str">`${this.heroesUrl}/?name=${term}`</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
    tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`found heroes matching "${term}"`</span><span class="pun">)),</span><span class="pln">
    catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="str">'searchHeroes'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[]))</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">如果没有搜索词，该方法立即返回一个空数组。 剩下的部分和 <code>getHeroes()</code> 很像。 唯一的不同点是 URL，它包含了一个由搜索词组成的查询字符串。</p><p translation-origin="off">The method returns immediately with an empty array if there is no search term. The rest of it closely resembles <code>getHeroes()</code>. The only significant difference is the URL, which includes a query string with the search term.</p><h3 id="add-search-to-the-dashboard" translation-result="on">为仪表盘添加搜索功能<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-search-to-the-dashboard"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="add-search-to-the-dashboard">Add search to the Dashboard<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#add-search-to-the-dashboard"><i class="material-icons">link</i></a></h3><p translation-result="on">打开 <code>DashboardComponent</code> 的<em>模板</em>并且把用于搜索英雄的元素 <code>&lt;app-hero-search&gt;</code> 添加到 <code>DashboardComponent</code> 模板的底部。</p><p translation-origin="off">Open the <code>DashboardComponent</code> <em>template</em> and Add the hero search element, <code>&lt;app-hero-search&gt;</code>, to the bottom of the <code>DashboardComponent</code> template.</p><code-example path="toh-pt6/src/app/dashboard/dashboard.component.html" header="src/app/dashboard/dashboard.component.html" linenums="false" ng-version="7.0.0"><div style="display:none">&lt;h3&gt;Top Heroes&lt;/h3&gt; &lt;div class="grid grid-pad"&gt; &lt;<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> *<a href="api/common/NgForOf" class="code-anchor">ngFor</a>="let hero of heroes" class="col-1-4" <a href="api/router/RouterLink" class="code-anchor">routerLink</a>="/detail/{{hero.id}}"&gt; &lt;div class="module hero"&gt; &lt;h4&gt;{{hero.name}}&lt;/h4&gt; &lt;/div&gt; &lt;/<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a>&gt; &lt;/div&gt; &lt;app-hero-search&gt;&lt;/app-hero-search&gt;</div><header class="ng-star-inserted">src/app/dashboard/dashboard.component.html</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/dashboard/dashboard.component.html">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="tag">&lt;h3&gt;</span><span class="pln">Top Heroes</span><span class="tag">&lt;/h3&gt;</span><span class="pln">
</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"grid grid-pad"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="pln"> *</span><a href="api/common/NgForOf" class="code-anchor"><span class="atn">ngFor</span></a><span class="pun">=</span><span class="atv">"let hero of heroes"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"col-1-4"</span><span class="pln">
      </span><a href="api/router/RouterLink" class="code-anchor"><span class="atn">routerLink</span></a><span class="pun">=</span><span class="atv">"/detail/{{hero.id}}"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"module hero"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;h4&gt;</span><span class="pln">{{hero.name}}</span><span class="tag">&lt;/h4&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;/</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;app-hero-search&gt;&lt;/app-hero-search&gt;</span></code>
    </pre></aio-code></code-example><p translation-result="on">这个模板看起来很像 <code>HeroesComponent</code> 模板中的 <code>*<a href="api/common/NgForOf" class="code-anchor">ngFor</a></code> 复写器。</p><p translation-origin="off">This template looks a lot like the <code>*<a href="api/common/NgForOf" class="code-anchor">ngFor</a></code> repeater in the <code>HeroesComponent</code> template.</p><p translation-result="on">很不幸，添加这个元素让本应用挂了。 Angular 找不到哪个组件的选择器能匹配上 <code>&lt;app-hero-search&gt;</code>。</p><p translation-origin="off">Unfortunately, adding this element breaks the app. Angular can't find a component with a selector that matches <code>&lt;app-hero-search&gt;</code>.</p><p translation-result="on"><code>HeroSearchComponent</code> 还不存在，这就解决。</p><p translation-origin="off">The <code>HeroSearchComponent</code> doesn't exist yet. Fix that.</p><h3 id="create-herosearchcomponent" translation-result="on">创建 <code>HeroSearchComponent</code><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#create-herosearchcomponent"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="create-herosearchcomponent">Create <em>HeroSearchComponent</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#create-herosearchcomponent"><i class="material-icons">link</i></a></h3><p translation-result="on">使用 CLI 创建一个 <code>HeroSearchComponent</code>。</p><p translation-origin="off">Create a <code>HeroSearchComponent</code> with the CLI.</p><code-example language="sh" class="code-shell" ng-version="7.0.0"><div style="display:none">ng generate component hero-search</div><aio-code><pre class="prettyprint lang-sh">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">ng generate component hero</span><span class="pun">-</span><span class="pln">search</span></code>
    </pre></aio-code></code-example><p translation-result="on">CLI 生成了 <code>HeroSearchComponent</code> 的三个文件，并把该组件添加到了 <code>AppModule</code> 的声明中。</p><p translation-origin="off">The CLI generates the three <code>HeroSearchComponent</code> files and adds the component to the <code>AppModule</code> declarations</p><p translation-result="on">把生成的 <code>HeroSearchComponent</code> 的<em>模板</em>改成一个输入框和一个匹配到的搜索结果的列表。代码如下：</p><p translation-origin="off">Replace the generated <code>HeroSearchComponent</code> <em>template</em> with a text box and a list of matching search results like this.</p><code-example path="toh-pt6/src/app/hero-search/hero-search.component.html" header="src/app/hero-search/hero-search.component.html" ng-version="7.0.0"><div style="display:none">&lt;div id="search-component"&gt; &lt;h4&gt;Hero Search&lt;/h4&gt; &lt;input #searchBox id="search-box" (input)="<a href="api/common/PlatformLocation#search" class="code-anchor">search</a>(searchBox.value)" /&gt; &lt;ul class="search-result"&gt; &lt;li *<a href="api/common/NgForOf" class="code-anchor">ngFor</a>="let hero of heroes$ | <a href="api/core/testing/async" class="code-anchor">async</a>" &gt; &lt;<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> <a href="api/router/RouterLink" class="code-anchor">routerLink</a>="/detail/{{hero.id}}"&gt; {{hero.name}} &lt;/<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a>&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/div&gt;</div><header class="ng-star-inserted">src/app/hero-search/hero-search.component.html</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero-search/hero-search.component.html">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><ol class="linenums"><li class="L0"><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"search-component"</span><span class="tag">&gt;</span></li><li class="L1"><span class="pln">  </span><span class="tag">&lt;h4&gt;</span><span class="pln">Hero Search</span><span class="tag">&lt;/h4&gt;</span></li><li class="L2"><span class="pln">&nbsp;</span></li><li class="L3"><span class="pln">  </span><span class="tag">&lt;input</span><span class="pln"> #</span><span class="atn">searchBox</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"search-box"</span><span class="pln"> (</span><span class="atn">input</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"</span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="atv">search</span></a><span class="atv">(searchBox.value)"</span><span class="pln"> </span><span class="tag">/&gt;</span></li><li class="L4"><span class="pln">&nbsp;</span></li><li class="L5"><span class="pln">  </span><span class="tag">&lt;ul</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"search-result"</span><span class="tag">&gt;</span></li><li class="L6"><span class="pln">    </span><span class="tag">&lt;li</span><span class="pln"> *</span><a href="api/common/NgForOf" class="code-anchor"><span class="atn">ngFor</span></a><span class="pun">=</span><span class="atv">"let hero of heroes$ | </span><a href="api/core/testing/async" class="code-anchor"><span class="atv">async</span></a><span class="atv">"</span><span class="pln"> </span><span class="tag">&gt;</span></li><li class="L7"><span class="pln">      </span><span class="tag">&lt;</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="pln"> </span><a href="api/router/RouterLink" class="code-anchor"><span class="atn">routerLink</span></a><span class="pun">=</span><span class="atv">"/detail/{{hero.id}}"</span><span class="tag">&gt;</span></li><li class="L8"><span class="pln">        {{hero.name}}</span></li><li class="L9"><span class="pln">      </span><span class="tag">&lt;/</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="tag">&gt;</span></li><li class="L0"><span class="pln">    </span><span class="tag">&lt;/li&gt;</span></li><li class="L1"><span class="pln">  </span><span class="tag">&lt;/ul&gt;</span></li><li class="L2"><span class="tag">&lt;/div&gt;</span></li></ol></code>
    </pre></aio-code></code-example><p translation-result="on">从下面的 <a href="tutorial/toh-pt6#herosearchcomponent">最终代码</a> 中把私有 CSS 样式添加到 <code>hero-search.component.css</code> 中。</p><p translation-origin="off">Add private CSS styles to <code>hero-search.component.css</code> as listed in the <a href="tutorial/toh-pt6#herosearchcomponent">final code review</a> below.</p><p translation-result="on">当用户在搜索框中输入时，一个 <em>keyup</em> 事件绑定会调用该组件的 <code><a href="api/common/PlatformLocation#search" class="code-anchor">search</a>()</code> 方法，并传入新的搜索框的值。</p><p translation-origin="off">As the user types in the search box, an <em>input</em> event binding calls the component's <code><a href="api/common/PlatformLocation#search" class="code-anchor">search</a>()</code> method with the new search box value.</p><a id="asyncpipe"></a><h3 id="asyncpipe"><em>AsyncPipe</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#asyncpipe"><i class="material-icons">link</i></a></h3><p translation-result="on">如你所愿，<code>*<a href="api/common/NgForOf" class="code-anchor">ngFor</a></code> 重复渲染出了这些英雄。</p><p translation-origin="off">As expected, the <code>*<a href="api/common/NgForOf" class="code-anchor">ngFor</a></code> repeats hero objects.</p><p translation-result="on">仔细看，你会发现 <code>*<a href="api/common/NgForOf" class="code-anchor">ngFor</a></code> 是在一个名叫 <code>heroes$</code> 的列表上迭代，而不是 <code>heroes</code>。</p><p translation-origin="off">Look closely and you'll see that the <code>*<a href="api/common/NgForOf" class="code-anchor">ngFor</a></code> iterates over a list called <code>heroes$</code>, not <code>heroes</code>.</p><code-example path="toh-pt6/src/app/hero-search/hero-search.component.html" region="async" ng-version="7.0.0"><div style="display:none">&lt;li *<a href="api/common/NgForOf" class="code-anchor">ngFor</a>="let hero of heroes$ | <a href="api/core/testing/async" class="code-anchor">async</a>" &gt;</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="tag">&lt;li</span><span class="pln"> *</span><a href="api/common/NgForOf" class="code-anchor"><span class="atn">ngFor</span></a><span class="pun">=</span><span class="atv">"let hero of heroes$ | </span><a href="api/core/testing/async" class="code-anchor"><span class="atv">async</span></a><span class="atv">"</span><span class="pln"> </span><span class="tag">&gt;</span></code>
    </pre></aio-code></code-example><p translation-result="on"><code>$</code> 是一个命名惯例，用来表明 <code>heroes$</code> 是一个 <code>Observable</code>，而不是数组。</p><p translation-origin="off">The <code>$</code> is a convention that indicates <code>heroes$</code> is an <code>Observable</code>, not an array.</p><p translation-result="on"><code>*<a href="api/common/NgForOf" class="code-anchor">ngFor</a></code> 不能直接使用 <code>Observable</code>。 不过，它后面还有一个管道字符（<code>|</code>），后面紧跟着一个 <code><a href="api/core/testing/async" class="code-anchor">async</a></code>，它表示 Angular 的 <code><a href="api/common/AsyncPipe" class="code-anchor">AsyncPipe</a></code>。</p><p translation-origin="off">The <code>*<a href="api/common/NgForOf" class="code-anchor">ngFor</a></code> can't do anything with an <code>Observable</code>. But there's also a pipe character (<code>|</code>) followed by <code><a href="api/core/testing/async" class="code-anchor">async</a></code>, which identifies Angular's <code><a href="api/common/AsyncPipe" class="code-anchor">AsyncPipe</a></code>.</p><p translation-result="on"><code><a href="api/common/AsyncPipe" class="code-anchor">AsyncPipe</a></code> 会自动订阅到 <code>Observable</code>，这样你就不用再在组件类中订阅了。</p><p translation-origin="off">The <code><a href="api/common/AsyncPipe" class="code-anchor">AsyncPipe</a></code> subscribes to an <code>Observable</code> automatically so you won't have to do so in the component class.</p><h3 id="fix-the-herosearchcomponent-class" translation-result="on">修正 <code>HeroSearchComponent</code> 类<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#fix-the-herosearchcomponent-class"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="fix-the-herosearchcomponent-class">Fix the <em>HeroSearchComponent</em> class<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#fix-the-herosearchcomponent-class"><i class="material-icons">link</i></a></h3><p translation-result="on">修改所生成的 <code>HeroSearchComponent</code> 类及其元数据，代码如下：</p><p translation-origin="off">Replace the generated <code>HeroSearchComponent</code> class and metadata as follows.</p><code-example path="toh-pt6/src/app/hero-search/hero-search.component.ts" header="src/app/hero-search/hero-search.component.ts" ng-version="7.0.0"><div style="display:none">import { <a href="api/core/Component" class="code-anchor">Component</a>, <a href="api/core/OnInit" class="code-anchor">OnInit</a> } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; import { Hero } from '../hero'; import { HeroService } from '../hero.service'; @<a href="api/core/Component" class="code-anchor">Component</a>({ selector: 'app-hero-search', templateUrl: './hero-search.component.html', <a href="api/core/Component#styleUrls" class="code-anchor">styleUrls</a>: [ './hero-search.component.css' ] }) export class HeroSearchComponent implements <a href="api/core/OnInit" class="code-anchor">OnInit</a> { heroes$: Observable&lt;Hero[]&gt;; private searchTerms = new Subject&lt;string&gt;(); constructor(private heroService: HeroService) {} // Push <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> term into the observable stream. <a href="api/common/PlatformLocation#search" class="code-anchor">search</a>(term: string): void { this.searchTerms.next(term); } ngOnInit(): void { this.heroes$ = this.searchTerms.pipe( // wait 300ms after each keystroke before considering the term debounceTime(300), // ignore new term if same as <a href="api/service-worker/UpdateActivatedEvent#previous" class="code-anchor">previous</a> term distinctUntilChanged(), // switch to new <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> observable each time the term <a href="api/core/QueryList#changes" class="code-anchor">changes</a> switchMap((term: string) =&gt; this.heroService.searchHeroes(term)), ); } }</div><header class="ng-star-inserted">src/app/hero-search/hero-search.component.ts</header><aio-code class="headed-code"><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/hero-search/hero-search.component.ts">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><ol class="linenums"><li class="L0"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><a href="api/core/Component" class="code-anchor"><span class="typ">Component</span></a><span class="pun">,</span><span class="pln"> </span><a href="api/core/OnInit" class="code-anchor"><span class="typ">OnInit</span></a><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span></li><li class="L1"><span class="pln">&nbsp;</span></li><li class="L2"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Subject</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'rxjs'</span><span class="pun">;</span></li><li class="L3"><span class="pln">&nbsp;</span></li><li class="L4"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span></li><li class="L5"><span class="pln">   debounceTime</span><span class="pun">,</span><span class="pln"> distinctUntilChanged</span><span class="pun">,</span><span class="pln"> switchMap</span></li><li class="L6"><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'rxjs/operators'</span><span class="pun">;</span></li><li class="L7"><span class="pln">&nbsp;</span></li><li class="L8"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Hero</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'../hero'</span><span class="pun">;</span></li><li class="L9"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">HeroService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'../hero.service'</span><span class="pun">;</span></li><li class="L0"><span class="pln">&nbsp;</span></li><li class="L1"><span class="lit">@</span><a href="api/core/Component" class="code-anchor"><span class="lit">Component</span></a><span class="pun">({</span></li><li class="L2"><span class="pln">  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-hero-search'</span><span class="pun">,</span></li><li class="L3"><span class="pln">  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./hero-search.component.html'</span><span class="pun">,</span></li><li class="L4"><span class="pln">  </span><a href="api/core/Component#styleUrls" class="code-anchor"><span class="pln">styleUrls</span></a><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="str">'./hero-search.component.css'</span><span class="pln"> </span><span class="pun">]</span></li><li class="L5"><span class="pun">})</span></li><li class="L6"><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HeroSearchComponent</span><span class="pln"> </span><span class="kwd">implements</span><span class="pln"> </span><a href="api/core/OnInit" class="code-anchor"><span class="typ">OnInit</span></a><span class="pln"> </span><span class="pun">{</span></li><li class="L7"><span class="pln">  heroes$</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;;</span></li><li class="L8"><span class="pln">  </span><span class="kwd">private</span><span class="pln"> searchTerms </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Subject</span><span class="str">&lt;string&gt;</span><span class="pun">();</span></li><li class="L9"><span class="pln">&nbsp;</span></li><li class="L0"><span class="pln">  </span><span class="kwd">constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> heroService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">HeroService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span></li><li class="L1"><span class="pln">&nbsp;</span></li><li class="L2"><span class="pln">  </span><span class="com">// Push </span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="com">a</span></a><span class="com"> </span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="com">search</span></a><span class="com"> term into the observable stream.</span></li><li class="L3"><span class="pln">  </span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="pln">search</span></a><span class="pun">(</span><span class="pln">term</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">):</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span></li><li class="L4"><span class="pln">    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">searchTerms</span><span class="pun">.</span><span class="kwd">next</span><span class="pun">(</span><span class="pln">term</span><span class="pun">);</span></li><li class="L5"><span class="pln">  </span><span class="pun">}</span></li><li class="L6"><span class="pln">&nbsp;</span></li><li class="L7"><span class="pln">  ngOnInit</span><span class="pun">():</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span></li><li class="L8"><span class="pln">    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroes$ </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">searchTerms</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span></li><li class="L9"><span class="pln">      </span><span class="com">// wait 300ms after each keystroke before considering the term</span></li><li class="L0"><span class="pln">      debounceTime</span><span class="pun">(</span><span class="lit">300</span><span class="pun">),</span></li><li class="L1"><span class="pln">&nbsp;</span></li><li class="L2"><span class="pln">      </span><span class="com">// ignore new term if same as </span><a href="api/service-worker/UpdateActivatedEvent#previous" class="code-anchor"><span class="com">previous</span></a><span class="com"> term</span></li><li class="L3"><span class="pln">      distinctUntilChanged</span><span class="pun">(),</span></li><li class="L4"><span class="pln">&nbsp;</span></li><li class="L5"><span class="pln">      </span><span class="com">// switch to new </span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="com">search</span></a><span class="com"> observable each time the term </span><a href="api/core/QueryList#changes" class="code-anchor"><span class="com">changes</span></a></li><li class="L6"><span class="pln">      switchMap</span><span class="pun">((</span><span class="pln">term</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroService</span><span class="pun">.</span><span class="pln">searchHeroes</span><span class="pun">(</span><span class="pln">term</span><span class="pun">)),</span></li><li class="L7"><span class="pln">    </span><span class="pun">);</span></li><li class="L8"><span class="pln">  </span><span class="pun">}</span></li><li class="L9"><span class="pun">}</span></li></ol></code>
    </pre></aio-code></code-example><p translation-result="on">注意，<code>heroes$</code> 声明为一个 <code>Observable</code></p><p translation-origin="off">Notice the declaration of <code>heroes$</code> as an <code>Observable</code></p><code-example path="toh-pt6/src/app/hero-search/hero-search.component.ts" region="heroes-stream" ng-version="7.0.0"><div style="display:none">heroes$: Observable&lt;Hero[]&gt;;</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">heroes$</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;;</span></code>
    </pre></aio-code></code-example><p translation-result="on">你将会在 <a href="tutorial/toh-pt6#search-pipe"><code>ngOnInit()</code></a>中设置它，在此之前，先仔细看看 <code>searchTerms</code> 的定义。</p><p translation-origin="off">You'll set it in <a href="tutorial/toh-pt6#search-pipe"><code>ngOnInit()</code></a>. Before you do, focus on the definition of <code>searchTerms</code>.</p><h3 id="the-searchterms-rxjs-subject" translation-result="on">RxJS <code>Subject</code> 类型的 <code>searchTerms</code><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#the-searchterms-rxjs-subject"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="the-searchterms-rxjs-subject">The <em>searchTerms</em> RxJS subject<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#the-searchterms-rxjs-subject"><i class="material-icons">link</i></a></h3><p translation-result="on"><code>searchTerms</code> 属性声明成了 RxJS 的 <code>Subject</code> 类型。</p><p translation-origin="off">The <code>searchTerms</code> property is declared as an RxJS <code>Subject</code>.</p><code-example path="toh-pt6/src/app/hero-search/hero-search.component.ts" region="searchTerms" ng-version="7.0.0"><div style="display:none">private searchTerms = new Subject&lt;string&gt;(); // Push <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> term into the observable stream. <a href="api/common/PlatformLocation#search" class="code-anchor">search</a>(term: string): void { this.searchTerms.next(term); }</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">private</span><span class="pln"> searchTerms </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Subject</span><span class="str">&lt;string&gt;</span><span class="pun">();</span><span class="pln">

</span><span class="com">// Push </span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="com">a</span></a><span class="com"> </span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="com">search</span></a><span class="com"> term into the observable stream.</span><span class="pln">
</span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="pln">search</span></a><span class="pun">(</span><span class="pln">term</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">):</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">searchTerms</span><span class="pun">.</span><span class="kwd">next</span><span class="pun">(</span><span class="pln">term</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on"><code>Subject</code> 既是可观察对象的数据源，本身也是 <code>Observable</code>。 你可以像订阅任何 <code>Observable</code> 一样订阅 <code>Subject</code>。</p><p translation-origin="off">A <code>Subject</code> is both a source of <em>observable</em> values and an <code>Observable</code> itself. You can subscribe to a <code>Subject</code> as you would any <code>Observable</code>.</p><p translation-result="on">你还可以通过调用它的 <code>next(value)</code> 方法往 <code>Observable</code> 中推送一些值，就像 <code><a href="api/common/PlatformLocation#search" class="code-anchor">search</a>()</code> 方法中一样。</p><p translation-origin="off">You can also push values into that <code>Observable</code> by calling its <code>next(value)</code> method as the <code><a href="api/common/PlatformLocation#search" class="code-anchor">search</a>()</code> method does.</p><p translation-result="on"><code><a href="api/common/PlatformLocation#search" class="code-anchor">search</a>()</code> 是通过对文本框的 <code>keystroke</code> 事件的<em>事件绑定</em>来调用的。</p><p translation-origin="off">The <code><a href="api/common/PlatformLocation#search" class="code-anchor">search</a>()</code> method is called via an <em>event binding</em> to the textbox's <code>input</code> event.</p><code-example path="toh-pt6/src/app/hero-search/hero-search.component.html" region="input" ng-version="7.0.0"><div style="display:none">&lt;input #searchBox id="search-box" (input)="<a href="api/common/PlatformLocation#search" class="code-anchor">search</a>(searchBox.value)" /&gt;</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="tag">&lt;input</span><span class="pln"> #</span><span class="atn">searchBox</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"search-box"</span><span class="pln"> (</span><span class="atn">input</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"</span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="atv">search</span></a><span class="atv">(searchBox.value)"</span><span class="pln"> </span><span class="tag">/&gt;</span></code>
    </pre></aio-code></code-example><p translation-result="on">每当用户在文本框中输入时，这个事件绑定就会使用文本框的值（搜索词）调用 <code><a href="api/common/PlatformLocation#search" class="code-anchor">search</a>()</code> 函数。 <code>searchTerms</code> 变成了一个能发出搜索词的稳定的流。</p><p translation-origin="off">Every time the user types in the textbox, the binding calls <code><a href="api/common/PlatformLocation#search" class="code-anchor">search</a>()</code> with the textbox value, a "search term". The <code>searchTerms</code> becomes an <code>Observable</code> emitting a steady stream of search terms.</p><a id="search-pipe"></a><h3 id="chaining-rxjs-operators" translation-result="on">串联 RxJS 操作符<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#chaining-rxjs-operators"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="chaining-rxjs-operators">Chaining RxJS operators<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#chaining-rxjs-operators"><i class="material-icons">link</i></a></h3><p translation-result="on">如果每当用户击键后就直接调用 <code>searchHeroes()</code> 将导致创建海量的 HTTP 请求，浪费服务器资源并消耗大量网络流量。</p><p translation-origin="off">Passing a new search term directly to the <code>searchHeroes()</code> after every user keystroke would create an excessive amount of HTTP requests, taxing server resources and burning through the cellular network data plan.</p><p translation-result="on">应该怎么做呢？<code>ngOnInit()</code> 往 <code>searchTerms</code> 这个可观察对象的处理管道中加入了一系列 RxJS 操作符，用以缩减对 <code>searchHeroes()</code> 的调用次数，并最终返回一个可及时给出英雄搜索结果的可观察对象（每次都是 <code>Hero[]</code> ）。</p><p translation-origin="off">Instead, the <code>ngOnInit()</code> method pipes the <code>searchTerms</code> observable through a sequence of RxJS operators that reduce the number of calls to the <code>searchHeroes()</code>, ultimately returning an observable of timely hero search results (each a <code>Hero[]</code>).</p><p translation-result="on">代码如下：</p><p translation-origin="off">Here's the code.</p><code-example path="toh-pt6/src/app/hero-search/hero-search.component.ts" region="search" ng-version="7.0.0"><div style="display:none">this.heroes$ = this.searchTerms.pipe( // wait 300ms after each keystroke before considering the term debounceTime(300), // ignore new term if same as <a href="api/service-worker/UpdateActivatedEvent#previous" class="code-anchor">previous</a> term distinctUntilChanged(), // switch to new <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> observable each time the term <a href="api/core/QueryList#changes" class="code-anchor">changes</a> switchMap((term: string) =&gt; this.heroService.searchHeroes(term)), );</div><aio-code><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroes$ </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">searchTerms</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
  </span><span class="com">// wait 300ms after each keystroke before considering the term</span><span class="pln">
  debounceTime</span><span class="pun">(</span><span class="lit">300</span><span class="pun">),</span><span class="pln">

  </span><span class="com">// ignore new term if same as </span><a href="api/service-worker/UpdateActivatedEvent#previous" class="code-anchor"><span class="com">previous</span></a><span class="com"> term</span><span class="pln">
  distinctUntilChanged</span><span class="pun">(),</span><span class="pln">

  </span><span class="com">// switch to new </span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="com">search</span></a><span class="com"> observable each time the term </span><a href="api/core/QueryList#changes" class="code-anchor"><span class="com">changes</span></a><span class="pln">
  switchMap</span><span class="pun">((</span><span class="pln">term</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroService</span><span class="pun">.</span><span class="pln">searchHeroes</span><span class="pun">(</span><span class="pln">term</span><span class="pun">)),</span><span class="pln">
</span><span class="pun">);</span></code>
    </pre></aio-code></code-example><ul><li><p translation-result="on">在传出最终字符串之前，<code>debounceTime(300)</code> 将会等待，直到新增字符串的事件暂停了 300 毫秒。 你实际发起请求的间隔永远不会小于 300ms。</p><p translation-origin="off"><code>debounceTime(300)</code> waits until the flow of new string events pauses for 300 milliseconds before passing along the latest string. You'll never make requests more frequently than 300ms.</p></li><li><p translation-result="on"> <code>distinctUntilChanged()</code> 会确保只在过滤条件变化时才发送请求。</p><p translation-origin="off"><code>distinctUntilChanged()</code> ensures that a request is sent only if the filter text changed.</p></li><li><p translation-result="on"> <code>switchMap()</code> 会为每个从 <code>debounce</code> 和 <code>distinctUntilChanged</code> 中通过的搜索词调用搜索服务。 它会取消并丢弃以前的搜索可观察对象，只保留最近的。</p><p translation-origin="off"><code>switchMap()</code> calls the search service for each search term that makes it through <code>debounce</code> and <code>distinctUntilChanged</code>. It cancels and discards previous search observables, returning only the latest search service observable.</p></li></ul><div class="alert is-helpful"><p translation-result="on">借助 <a href="http://www.learnrxjs.io/operators/transformation/switchmap.html">switchMap 操作符</a>， 每个有效的击键事件都会触发一次 <code><a href="api/common/http/HttpClient#get" class="code-anchor">HttpClient.get()</a></code> 方法调用。 即使在每个请求之间都有至少 300ms 的间隔，仍然可能会同时存在多个尚未返回的 HTTP 请求。</p><p translation-origin="off">With the <a href="http://www.learnrxjs.io/operators/transformation/switchmap.html">switchMap operator</a>, every qualifying key event can trigger an <code><a href="api/common/http/HttpClient#get" class="code-anchor">HttpClient.get()</a></code> method call. Even with a 300ms pause between requests, you could have multiple HTTP requests in flight and they may not return in the order sent.</p><p translation-result="on"> <code>switchMap()</code> 会记住原始的请求顺序，只会返回最近一次 HTTP 方法调用的结果。 以前的那些请求都会被取消和舍弃。</p><p translation-origin="off"> <code>switchMap()</code> preserves the original request order while returning only the observable from the most recent HTTP method call. Results from prior calls are canceled and discarded.</p><p translation-result="on">注意，<em>取消</em>前一个 <code>searchHeroes()</code> 可观察对象并不会中止尚未完成的 HTTP 请求。 那些不想要的结果只会在它们抵达应用代码之前被舍弃。</p><p translation-origin="off">Note that <em>canceling</em> a previous <code>searchHeroes()</code> <em>Observable</em> doesn't actually abort a pending HTTP request. Unwanted results are simply discarded before they reach your application code.</p></div><p translation-result="on">记住，组件类中并没有订阅 <code>heroes$</code> 这个可观察对象，而是由模板中的 <a href="tutorial/toh-pt6#asyncpipe"><code>AsyncPipe</code></a>完成的。</p><p translation-origin="off">Remember that the component <em>class</em> does not subscribe to the <code>heroes$</code> <em>observable</em>. That's the job of the <a href="tutorial/toh-pt6#asyncpipe"><code>AsyncPipe</code></a>in the template.</p><h4 id="try-it" translation-result="on">试试看<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#try-it"><i class="material-icons">link</i></a></h4><h4 translation-origin="off" id="try-it">Try it<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#try-it"><i class="material-icons">link</i></a></h4><p translation-result="on">再次运行本应用。在这个 <em>仪表盘</em> 中，在搜索框中输入一些文字。如果你输入的字符匹配上了任何现有英雄的名字，你将会看到如下效果：</p><p translation-origin="off">Run the app again. In the <em>Dashboard</em>, enter some text in the search box. If you enter characters that match any existing hero names, you'll see something like this.</p><figure><img src="generated/images/guide/toh/toh-hero-search.png" alt="Hero Search Component" width="240" height="224"></figure><h2 id="final-code-review" translation-result="on">查看最终代码<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#final-code-review"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="final-code-review">Final code review<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#final-code-review"><i class="material-icons">link</i></a></h2><p translation-result="on">你的应用现在变成了这样：<live-example ng-version="7.0.0"><span style="display:none"></span><span><span class="ng-star-inserted"><a target="_blank" href="generated/live-examples/toh-pt6/stackblitz.html" title="在线例子">在线例子</a><span class="ng-star-inserted"> / <a download="" title="下载范例" href="generated/zips/toh-pt6/toh-pt6.zip">下载范例</a></span></span></span></live-example>。</p><p translation-origin="off">Your app should look like this<live-example ng-version="7.0.0"><span style="display:none"></span><span><span class="ng-star-inserted"><a target="_blank" href="generated/live-examples/toh-pt6/stackblitz.html" title="在线例子">在线例子</a><span class="ng-star-inserted"> / <a download="" title="下载范例" href="generated/zips/toh-pt6/toh-pt6.zip">下载范例</a></span></span></span></live-example>.</p><p translation-result="on">本文讨论过的代码文件如下（都位于 <code>src/app/</code> 文件夹中）。</p><p translation-origin="off">Here are the code files discussed on this page (all in the <code>src/app/</code> folder).</p><a id="heroservice"></a> <a id="inmemorydataservice"></a> <a id="appmodule"></a><h4 id="heroservice-inmemorydataservice-appmodule"><em>HeroService</em>, <em>InMemoryDataService</em>, <em>AppModule</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#heroservice-inmemorydataservice-appmodule"><i class="material-icons">link</i></a></h4><code-tabs ng-version="7.0.0"><div style="display:none"><code-pane header="hero.service.ts" path="toh-pt6/src/app/hero.service.ts">import { <a href="api/core/Injectable" class="code-anchor">Injectable</a> } from '@angular/core'; import { <a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a>, <a href="api/common/http/HttpHeaders" class="code-anchor">HttpHeaders</a> } from '@angular/common/<a href="api/common/http" class="code-anchor">http</a>'; import { Observable, of } from 'rxjs'; import { catchError, map, tap } from 'rxjs/operators'; import { Hero } from './hero'; import { MessageService } from './message.service'; const httpOptions = { headers: new <a href="api/common/http/HttpHeaders" class="code-anchor">HttpHeaders</a>({ 'Content-Type': 'application/json' }) }; @<a href="api/core/Injectable" class="code-anchor">Injectable</a>({ <a href="api/core/Injectable#providedIn" class="code-anchor">providedIn</a>: 'root' }) export class HeroService { private heroesUrl = 'api/heroes'; // <a href="api/core/SecurityContext#URL" class="code-anchor">URL</a> to web api constructor( private <a href="api/common/http" class="code-anchor">http</a>: <a href="api/common/http/HttpClient" class="code-anchor">HttpClient</a>, private messageService: MessageService) { } /** GET heroes from the server */ getHeroes (): Observable&lt;Hero[]&gt; { return this.http.get&lt;Hero[]&gt;(this.heroesUrl) .pipe( tap(_ =&gt; this.log('fetched heroes')), catchError(this.handleError&lt;Hero[]&gt;('getHeroes', [])) ); } /** GET hero by id. Return `undefined` when id not found */ getHeroNo404&lt;<a href="api/router/Data" class="code-anchor">Data</a>&gt;(id: number): Observable&lt;Hero&gt; { const url = `${this.heroesUrl}/?id=${id}`; return this.http.get&lt;Hero[]&gt;(url) .pipe( map(heroes =&gt; heroes[0]), // returns <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> {0|1} element array tap(h =&gt; { const outcome = h ? `fetched` : `did not find`; this.log(`${outcome} hero id=${id}`); }), catchError(this.handleError&lt;Hero&gt;(`getHero id=${id}`)) ); } /** GET hero by id. Will 404 if id not found */ getHero(id: number): Observable&lt;Hero&gt; { const url = `${this.heroesUrl}/${id}`; return this.http.get&lt;Hero&gt;(url).pipe( tap(_ =&gt; this.log(`fetched hero id=${id}`)), catchError(this.handleError&lt;Hero&gt;(`getHero id=${id}`)) ); } /* GET heroes whose name contains <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> term */ searchHeroes(term: string): Observable&lt;Hero[]&gt; { if (!term.trim()) { // if not <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> term, return empty hero array. return of([]); } return this.http.get&lt;Hero[]&gt;(`${this.heroesUrl}/?name=${term}`).pipe( tap(_ =&gt; this.log(`found heroes matching "${term}"`)), catchError(this.handleError&lt;Hero[]&gt;('searchHeroes', [])) ); } //////// Save methods ////////// /** POST: add <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> new hero to the server */ addHero (hero: Hero): Observable&lt;Hero&gt; { return this.http.post&lt;Hero&gt;(this.heroesUrl, hero, httpOptions).pipe( tap((newHero: Hero) =&gt; this.log(`added hero w/ id=${newHero.id}`)), catchError(this.handleError&lt;Hero&gt;('addHero')) ); } /** DELETE: delete the hero from the server */ deleteHero (hero: Hero | number): Observable&lt;Hero&gt; { const id = typeof hero === 'number' ? hero : hero.id; const url = `${this.heroesUrl}/${id}`; return this.http.delete&lt;Hero&gt;(url, httpOptions).pipe( tap(_ =&gt; this.log(`deleted hero id=${id}`)), catchError(this.handleError&lt;Hero&gt;('deleteHero')) ); } /** PUT: <a href="api/forms/NgModel#update" class="code-anchor">update</a> the hero on the server */ updateHero (hero: Hero): Observable&lt;any&gt; { return this.http.put(this.heroesUrl, hero, httpOptions).pipe( tap(_ =&gt; this.log(`updated hero id=${hero.id}`)), catchError(this.handleError&lt;any&gt;('updateHero')) ); } /** * Handle <a href="api/http/Http" class="code-anchor">Http</a> operation that failed. * Let the app continue. * @param operation - name of the operation that failed * @param result - <a href="api/animations/AnimationQueryOptions#optional" class="code-anchor">optional</a> value to return as the observable result */ private handleError&lt;T&gt; (operation = 'operation', result?: T) { return (error: any): Observable&lt;T&gt; =&gt; { // TODO: send the error to remote logging infrastructure console.error(error); // <a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor">log</a> to console instead // TODO: better job of transforming error for user consumption this.log(`${operation} failed: ${error.message}`); // Let the app keep running by returning an empty result. return of(result as T); }; } /** Log <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> HeroService <a href="api/common/http/HttpErrorResponse#message" class="code-anchor">message</a> with the MessageService */ private <a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor">log</a>(<a href="api/common/http/HttpErrorResponse#message" class="code-anchor">message</a>: string) { this.messageService.add(`HeroService: ${<a href="api/common/http/HttpErrorResponse#message" class="code-anchor">message</a>}`); } }</code-pane><code-pane header="in-memory-data.service.ts" path="toh-pt6/src/app/in-memory-data.service.ts">import { InMemoryDbService } from 'angular-in-memory-web-api'; import { Hero } from './hero'; import { <a href="api/core/Injectable" class="code-anchor">Injectable</a> } from '@angular/core'; @<a href="api/core/Injectable" class="code-anchor">Injectable</a>({ <a href="api/core/Injectable#providedIn" class="code-anchor">providedIn</a>: 'root', }) export class InMemoryDataService implements InMemoryDbService { createDb() { const heroes = [ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, { id: 13, name: 'Bombasto' }, { id: 14, name: 'Celeritas' }, { id: 15, name: 'Magneta' }, { id: 16, name: 'RubberMan' }, { id: 17, name: 'Dynama' }, { id: 18, name: 'Dr IQ' }, { id: 19, name: 'Magma' }, { id: 20, name: 'Tornado' } ]; return {heroes}; } // Overrides the genId method to ensure that <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> hero always has an id. // If the heroes array is empty, // the method below returns the initial number (11). // if the heroes array is not empty, the method below returns the highest // hero id + 1. genId(heroes: Hero[]): number { return heroes.length &gt; 0 ? Math.max(...heroes.map(hero =&gt; hero.id)) + 1 : 11; } }</code-pane><code-pane header="app.module.ts" path="toh-pt6/src/app/app.module.ts">import { <a href="api/core/NgModule" class="code-anchor">NgModule</a> } from '@angular/core'; import { <a href="api/platform-browser/BrowserModule" class="code-anchor">BrowserModule</a> } from '@angular/platform-browser'; import { <a href="api/forms/FormsModule" class="code-anchor">FormsModule</a> } from '@angular/forms'; import { <a href="api/common/http/HttpClientModule" class="code-anchor">HttpClientModule</a> } from '@angular/common/<a href="api/common/http" class="code-anchor">http</a>'; import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; import { InMemoryDataService } from './in-memory-data.service'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { DashboardComponent } from './dashboard/dashboard.component'; import { HeroDetailComponent } from './hero-detail/hero-detail.component'; import { HeroesComponent } from './heroes/heroes.component'; import { HeroSearchComponent } from './hero-search/hero-search.component'; import { MessagesComponent } from './<a href="api/service-worker/SwPush#messages" class="code-anchor">messages</a>/messages.component'; @<a href="api/core/NgModule" class="code-anchor">NgModule</a>({ <a href="api/core/NgModule#imports" class="code-anchor">imports</a>: [ <a href="api/platform-browser/BrowserModule" class="code-anchor">BrowserModule</a>, <a href="api/forms/FormsModule" class="code-anchor">FormsModule</a>, AppRoutingModule, <a href="api/common/http/HttpClientModule" class="code-anchor">HttpClientModule</a>, // The HttpClientInMemoryWebApiModule module intercepts HTTP requests // and returns simulated server responses. // Remove it when <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> real server is ready to receive requests. HttpClientInMemoryWebApiModule.forRoot( InMemoryDataService, { dataEncapsulation: false } ) ], <a href="api/core/NgModule#declarations" class="code-anchor">declarations</a>: [ AppComponent, DashboardComponent, HeroesComponent, HeroDetailComponent, MessagesComponent, HeroSearchComponent ], <a href="api/core/NgModule#bootstrap" class="code-anchor">bootstrap</a>: [ AppComponent ] }) export class AppModule { }</code-pane></div><mat-card class="mat-card"><mat-tab-group class="code-tab-group mat-tab-group mat-primary" disableripple=""><mat-tab-header class="mat-tab-header"><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-before mat-elevation-z4 mat-ripple mat-tab-header-pagination-disabled" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div><div class="mat-tab-label-container"><div class="mat-tab-list" role="tablist" style="transform:translateX(0)"><div class="mat-tab-labels"><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple mat-tab-label-active ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-0-0" tabindex="0" aria-posinset="1" aria-setsize="3" aria-controls="mat-tab-content-0-0" aria-selected="true" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">hero.service.ts</span></div></div><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-0-1" tabindex="-1" aria-posinset="2" aria-setsize="3" aria-controls="mat-tab-content-0-1" aria-selected="false" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">in-memory-data.service.ts</span></div></div><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-0-2" tabindex="-1" aria-posinset="3" aria-setsize="3" aria-controls="mat-tab-content-0-2" aria-selected="false" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">app.module.ts</span></div></div></div><mat-ink-bar class="mat-ink-bar" style="visibility:visible;left:0;width:160px"></mat-ink-bar></div></div><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-after mat-elevation-z4 mat-ripple mat-tab-header-pagination-disabled" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div></mat-tab-header><div class="mat-tab-body-wrapper"><mat-tab-body class="mat-tab-body ng-tns-c12-2 mat-tab-body-active ng-star-inserted" role="tabpanel" id="mat-tab-content-0-0" aria-labelledby="mat-tab-label-0-0"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:none"><aio-code class="ng-star-inserted" style=""><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from hero.service.ts">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><ol class="linenums"><li class="L0"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><a href="api/core/Injectable" class="code-anchor"><span class="typ">Injectable</span></a><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span></li><li class="L1"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><a href="api/common/http/HttpClient" class="code-anchor"><span class="typ">HttpClient</span></a><span class="pun">,</span><span class="pln"> </span><a href="api/common/http/HttpHeaders" class="code-anchor"><span class="typ">HttpHeaders</span></a><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/common/</span><a href="api/common/http" class="code-anchor"><span class="str">http</span></a><span class="str">'</span><span class="pun">;</span></li><li class="L2"><span class="pln">&nbsp;</span></li><li class="L3"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">,</span><span class="pln"> of </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'rxjs'</span><span class="pun">;</span></li><li class="L4"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> catchError</span><span class="pun">,</span><span class="pln"> map</span><span class="pun">,</span><span class="pln"> tap </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'rxjs/operators'</span><span class="pun">;</span></li><li class="L5"><span class="pln">&nbsp;</span></li><li class="L6"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Hero</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'./hero'</span><span class="pun">;</span></li><li class="L7"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MessageService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'./message.service'</span><span class="pun">;</span></li><li class="L8"><span class="pln">&nbsp;</span></li><li class="L9"><span class="kwd">const</span><span class="pln"> httpOptions </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span></li><li class="L0"><span class="pln">  headers</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><a href="api/common/http/HttpHeaders" class="code-anchor"><span class="typ">HttpHeaders</span></a><span class="pun">({</span><span class="pln"> </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pln"> </span><span class="pun">})</span></li><li class="L1"><span class="pun">};</span></li><li class="L2"><span class="pln">&nbsp;</span></li><li class="L3"><span class="lit">@</span><a href="api/core/Injectable" class="code-anchor"><span class="lit">Injectable</span></a><span class="pun">({</span><span class="pln"> </span><a href="api/core/Injectable#providedIn" class="code-anchor"><span class="pln">providedIn</span></a><span class="pun">:</span><span class="pln"> </span><span class="str">'root'</span><span class="pln"> </span><span class="pun">})</span></li><li class="L4"><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HeroService</span><span class="pln"> </span><span class="pun">{</span></li><li class="L5"><span class="pln">&nbsp;</span></li><li class="L6"><span class="pln">  </span><span class="kwd">private</span><span class="pln"> heroesUrl </span><span class="pun">=</span><span class="pln"> </span><span class="str">'api/heroes'</span><span class="pun">;</span><span class="pln">  </span><span class="com">// </span><a href="api/core/SecurityContext#URL" class="code-anchor"><span class="com">URL</span></a><span class="com"> to web api</span></li><li class="L7"><span class="pln">&nbsp;</span></li><li class="L8"><span class="pln">  </span><span class="kwd">constructor</span><span class="pun">(</span></li><li class="L9"><span class="pln">    </span><span class="kwd">private</span><span class="pln"> </span><a href="api/common/http" class="code-anchor"><span class="pln">http</span></a><span class="pun">:</span><span class="pln"> </span><a href="api/common/http/HttpClient" class="code-anchor"><span class="typ">HttpClient</span></a><span class="pun">,</span></li><li class="L0"><span class="pln">    </span><span class="kwd">private</span><span class="pln"> messageService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">MessageService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></li><li class="L1"><span class="pln">&nbsp;</span></li><li class="L2"><span class="pln">  </span><span class="com">/** GET heroes from the server */</span></li><li class="L3"><span class="pln">  getHeroes </span><span class="pun">():</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L4"><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroesUrl</span><span class="pun">)</span></li><li class="L5"><span class="pln">      </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span></li><li class="L6"><span class="pln">        tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'fetched heroes'</span><span class="pun">)),</span></li><li class="L7"><span class="pln">        catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="str">'getHeroes'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[]))</span></li><li class="L8"><span class="pln">      </span><span class="pun">);</span></li><li class="L9"><span class="pln">  </span><span class="pun">}</span></li><li class="L0"><span class="pln">&nbsp;</span></li><li class="L1"><span class="pln">  </span><span class="com">/** GET hero by id. Return `undefined` when id not found */</span></li><li class="L2"><span class="pln">  getHeroNo404</span><span class="pun">&lt;</span><a href="api/router/Data" class="code-anchor"><span class="typ">Data</span></a><span class="pun">&gt;(</span><span class="pln">id</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L3"><span class="pln">    </span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">`${this.heroesUrl}/?id=${id}`</span><span class="pun">;</span></li><li class="L4"><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="pln">url</span><span class="pun">)</span></li><li class="L5"><span class="pln">      </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span></li><li class="L6"><span class="pln">        map</span><span class="pun">(</span><span class="pln">heroes </span><span class="pun">=&gt;</span><span class="pln"> heroes</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]),</span><span class="pln"> </span><span class="com">// returns </span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="com">a</span></a><span class="com"> {0|1} element array</span></li><li class="L7"><span class="pln">        tap</span><span class="pun">(</span><span class="pln">h </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L8"><span class="pln">          </span><span class="kwd">const</span><span class="pln"> outcome </span><span class="pun">=</span><span class="pln"> h </span><span class="pun">?</span><span class="pln"> </span><span class="str">`fetched`</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">`did not find`</span><span class="pun">;</span></li><li class="L9"><span class="pln">          </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`${outcome} hero id=${id}`</span><span class="pun">);</span></li><li class="L0"><span class="pln">        </span><span class="pun">}),</span></li><li class="L1"><span class="pln">        catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="str">`getHero id=${id}`</span><span class="pun">))</span></li><li class="L2"><span class="pln">      </span><span class="pun">);</span></li><li class="L3"><span class="pln">  </span><span class="pun">}</span></li><li class="L4"><span class="pln">&nbsp;</span></li><li class="L5"><span class="pln">  </span><span class="com">/** GET hero by id. Will 404 if id not found */</span></li><li class="L6"><span class="pln">  getHero</span><span class="pun">(</span><span class="pln">id</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L7"><span class="pln">    </span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">`${this.heroesUrl}/${id}`</span><span class="pun">;</span></li><li class="L8"><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="pln">url</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span></li><li class="L9"><span class="pln">      tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`fetched hero id=${id}`</span><span class="pun">)),</span></li><li class="L0"><span class="pln">      catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="str">`getHero id=${id}`</span><span class="pun">))</span></li><li class="L1"><span class="pln">    </span><span class="pun">);</span></li><li class="L2"><span class="pln">  </span><span class="pun">}</span></li><li class="L3"><span class="pln">&nbsp;</span></li><li class="L4"><span class="pln">  </span><span class="com">/* GET heroes whose name contains </span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="com">search</span></a><span class="com"> term */</span></li><li class="L5"><span class="pln">  searchHeroes</span><span class="pun">(</span><span class="pln">term</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L6"><span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">term</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span></li><li class="L7"><span class="pln">      </span><span class="com">// if not </span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="com">search</span></a><span class="com"> term, return empty hero array.</span></li><li class="L8"><span class="pln">      </span><span class="kwd">return</span><span class="pln"> of</span><span class="pun">([]);</span></li><li class="L9"><span class="pln">    </span><span class="pun">}</span></li><li class="L0"><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="str">`${this.heroesUrl}/?name=${term}`</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span></li><li class="L1"><span class="pln">      tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`found heroes matching "${term}"`</span><span class="pun">)),</span></li><li class="L2"><span class="pln">      catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">[]&gt;(</span><span class="str">'searchHeroes'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[]))</span></li><li class="L3"><span class="pln">    </span><span class="pun">);</span></li><li class="L4"><span class="pln">  </span><span class="pun">}</span></li><li class="L5"><span class="pln">&nbsp;</span></li><li class="L6"><span class="pln">  </span><span class="com">//////// Save methods //////////</span></li><li class="L7"><span class="pln">&nbsp;</span></li><li class="L8"><span class="pln">  </span><span class="com">/** POST: add </span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="com">a</span></a><span class="com"> new hero to the server */</span></li><li class="L9"><span class="pln">  addHero </span><span class="pun">(</span><span class="pln">hero</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L0"><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="pln">post</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroesUrl</span><span class="pun">,</span><span class="pln"> hero</span><span class="pun">,</span><span class="pln"> httpOptions</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span></li><li class="L1"><span class="pln">      tap</span><span class="pun">((</span><span class="pln">newHero</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`added hero w/ id=${newHero.id}`</span><span class="pun">)),</span></li><li class="L2"><span class="pln">      catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="str">'addHero'</span><span class="pun">))</span></li><li class="L3"><span class="pln">    </span><span class="pun">);</span></li><li class="L4"><span class="pln">  </span><span class="pun">}</span></li><li class="L5"><span class="pln">&nbsp;</span></li><li class="L6"><span class="pln">  </span><span class="com">/** DELETE: delete the hero from the server */</span></li><li class="L7"><span class="pln">  deleteHero </span><span class="pun">(</span><span class="pln">hero</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L8"><span class="pln">    </span><span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">typeof</span><span class="pln"> hero </span><span class="pun">===</span><span class="pln"> </span><span class="str">'number'</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> hero </span><span class="pun">:</span><span class="pln"> hero</span><span class="pun">.</span><span class="pln">id</span><span class="pun">;</span></li><li class="L9"><span class="pln">    </span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">`${this.heroesUrl}/${id}`</span><span class="pun">;</span></li><li class="L0"><span class="pln">&nbsp;</span></li><li class="L1"><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> httpOptions</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span></li><li class="L2"><span class="pln">      tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`deleted hero id=${id}`</span><span class="pun">)),</span></li><li class="L3"><span class="pln">      catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="pun">&lt;</span><span class="typ">Hero</span><span class="pun">&gt;(</span><span class="str">'deleteHero'</span><span class="pun">))</span></li><li class="L4"><span class="pln">    </span><span class="pun">);</span></li><li class="L5"><span class="pln">  </span><span class="pun">}</span></li><li class="L6"><span class="pln">&nbsp;</span></li><li class="L7"><span class="pln">  </span><span class="com">/** PUT: </span><a href="api/forms/NgModel#update" class="code-anchor"><span class="com">update</span></a><span class="com"> the hero on the server */</span></li><li class="L8"><span class="pln">  updateHero </span><span class="pun">(</span><span class="pln">hero</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Hero</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="str">&lt;any&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L9"><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">heroesUrl</span><span class="pun">,</span><span class="pln"> hero</span><span class="pun">,</span><span class="pln"> httpOptions</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span></li><li class="L0"><span class="pln">      tap</span><span class="pun">(</span><span class="pln">_ </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`updated hero id=${hero.id}`</span><span class="pun">)),</span></li><li class="L1"><span class="pln">      catchError</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleError</span><span class="str">&lt;any&gt;</span><span class="pun">(</span><span class="str">'updateHero'</span><span class="pun">))</span></li><li class="L2"><span class="pln">    </span><span class="pun">);</span></li><li class="L3"><span class="pln">  </span><span class="pun">}</span></li><li class="L4"><span class="pln">&nbsp;</span></li><li class="L5"><span class="pln">  </span><span class="com">/**</span></li><li class="L6"><span class="com">   * Handle </span><a href="api/http/Http" class="code-anchor"><span class="com">Http</span></a><span class="com"> operation that failed.</span></li><li class="L7"><span class="com">   * Let the app continue.</span></li><li class="L8"><span class="com">   * @param operation - name of the operation that failed</span></li><li class="L9"><span class="com">   * @param result - </span><a href="api/animations/AnimationQueryOptions#optional" class="code-anchor"><span class="com">optional</span></a><span class="com"> value to return as the observable result</span></li><li class="L0"><span class="com">   */</span></li><li class="L1"><span class="pln">  </span><span class="kwd">private</span><span class="pln"> handleError</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">operation </span><span class="pun">=</span><span class="pln"> </span><span class="str">'operation'</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">?:</span><span class="pln"> T</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></li><li class="L2"><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span></li><li class="L3"><span class="pln">&nbsp;</span></li><li class="L4"><span class="pln">      </span><span class="com">// TODO: send the error to remote logging infrastructure</span></li><li class="L5"><span class="pln">      console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">error</span><span class="pun">);</span><span class="pln"> </span><span class="com">// </span><a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor"><span class="com">log</span></a><span class="com"> to console instead</span></li><li class="L6"><span class="pln">&nbsp;</span></li><li class="L7"><span class="pln">      </span><span class="com">// TODO: better job of transforming error for user consumption</span></li><li class="L8"><span class="pln">      </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">`${operation} failed: ${error.message}`</span><span class="pun">);</span></li><li class="L9"><span class="pln">&nbsp;</span></li><li class="L0"><span class="pln">      </span><span class="com">// Let the app keep running by returning an empty result.</span></li><li class="L1"><span class="pln">      </span><span class="kwd">return</span><span class="pln"> of</span><span class="pun">(</span><span class="pln">result </span><span class="kwd">as</span><span class="pln"> T</span><span class="pun">);</span></li><li class="L2"><span class="pln">    </span><span class="pun">};</span></li><li class="L3"><span class="pln">  </span><span class="pun">}</span></li><li class="L4"><span class="pln">&nbsp;</span></li><li class="L5"><span class="pln">  </span><span class="com">/** Log </span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="com">a</span></a><span class="com"> HeroService </span><a href="api/common/http/HttpErrorResponse#message" class="code-anchor"><span class="com">message</span></a><span class="com"> with the MessageService */</span></li><li class="L6"><span class="pln">  </span><span class="kwd">private</span><span class="pln"> </span><a href="api/animations/browser/testing/MockAnimationDriver#log" class="code-anchor"><span class="pln">log</span></a><span class="pun">(</span><a href="api/common/http/HttpErrorResponse#message" class="code-anchor"><span class="pln">message</span></a><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></li><li class="L7"><span class="pln">    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">messageService</span><span class="pun">.</span><span class="kwd">add</span><span class="pun">(</span><span class="str">`HeroService: ${</span><a href="api/common/http/HttpErrorResponse#message" class="code-anchor"><span class="str">message</span></a><span class="str">}`</span><span class="pun">);</span></li><li class="L8"><span class="pln">  </span><span class="pun">}</span></li><li class="L9"><span class="pun">}</span></li></ol></code>
    </pre></aio-code></div></mat-tab-body><mat-tab-body class="mat-tab-body ng-tns-c12-3 ng-star-inserted" role="tabpanel" id="mat-tab-content-0-1" aria-labelledby="mat-tab-label-0-1"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:translate3d(100%,0,0);min-height:1px"></div></mat-tab-body><mat-tab-body class="mat-tab-body ng-tns-c12-4 ng-star-inserted" role="tabpanel" id="mat-tab-content-0-2" aria-labelledby="mat-tab-label-0-2"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:translate3d(100%,0,0);min-height:1px"></div></mat-tab-body></div></mat-tab-group></mat-card></code-tabs><a id="heroescomponent"></a><h4 id="heroescomponent"><em>HeroesComponent</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#heroescomponent"><i class="material-icons">link</i></a></h4><code-tabs ng-version="7.0.0"><div style="display:none"><code-pane header="heroes/heroes.component.html" path="toh-pt6/src/app/heroes/heroes.component.html">&lt;h2&gt;My Heroes&lt;/h2&gt; &lt;div&gt; &lt;label&gt;Hero name: &lt;input #heroName /&gt; &lt;/label&gt; &lt;!-- (click) passes input value to add() and then clears the input --&gt; &lt;button (click)="add(heroName.value); heroName.value=''"&gt; add &lt;/button&gt; &lt;/div&gt; &lt;ul class="heroes"&gt; &lt;li *<a href="api/common/NgForOf" class="code-anchor">ngFor</a>="let hero of heroes"&gt; &lt;<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> <a href="api/router/RouterLink" class="code-anchor">routerLink</a>="/detail/{{hero.id}}"&gt; &lt;span class="badge"&gt;{{hero.id}}&lt;/span&gt; {{hero.name}} &lt;/<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a>&gt; &lt;button class="delete" title="delete hero" (click)="delete(hero)"&gt;x&lt;/button&gt; &lt;/li&gt; &lt;/ul&gt;</code-pane><code-pane header="heroes/heroes.component.ts" path="toh-pt6/src/app/heroes/heroes.component.ts">import { <a href="api/core/Component" class="code-anchor">Component</a>, <a href="api/core/OnInit" class="code-anchor">OnInit</a> } from '@angular/core'; import { Hero } from '../hero'; import { HeroService } from '../hero.service'; @<a href="api/core/Component" class="code-anchor">Component</a>({ selector: 'app-heroes', templateUrl: './heroes.component.html', <a href="api/core/Component#styleUrls" class="code-anchor">styleUrls</a>: ['./heroes.component.css'] }) export class HeroesComponent implements <a href="api/core/OnInit" class="code-anchor">OnInit</a> { heroes: Hero[]; constructor(private heroService: HeroService) { } ngOnInit() { this.getHeroes(); } getHeroes(): void { this.heroService.getHeroes() .subscribe(heroes =&gt; this.heroes = heroes); } add(name: string): void { name = name.trim(); if (!name) { return; } this.heroService.addHero({ name } as Hero) .subscribe(hero =&gt; { this.heroes.push(hero); }); } delete(hero: Hero): void { this.heroes = this.heroes.filter(h =&gt; h !== hero); this.heroService.deleteHero(hero).subscribe(); } }</code-pane><code-pane header="heroes/heroes.component.css" path="toh-pt6/src/app/heroes/heroes.component.css">/* HeroesComponent's private CSS styles */ .heroes { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em; } .heroes li { <a href="api/router/Scroll#position" class="code-anchor">position</a>: relative; cursor: pointer; background-color: #EEE; margin: .5em; padding: .3em 0; height: 1.6em; border-radius: 4px; } .heroes li:hover { color: #607D8B; background-color: #DDD; left: .1em; } .heroes <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> { color: #888; text-decoration: none; <a href="api/router/Scroll#position" class="code-anchor">position</a>: relative; display: block; width: 250px; } .heroes <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a>:hover { color:#607D8B; } .heroes .badge { display: inline-block; font-size: small; color: white; padding: 0.8em 0.7em 0 0.7em; background-color: #607D8B; line-height: 1em; <a href="api/router/Scroll#position" class="code-anchor">position</a>: relative; left: -1px; top: -4px; height: 1.8em; min-width: 16px; text-align: right; margin-right: .8em; border-radius: 4px 0 0 4px; } button { background-color: #eee; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; cursor: hand; font-family: Arial; } button:hover { background-color: #cfd8dc; } button.delete { <a href="api/router/Scroll#position" class="code-anchor">position</a>: relative; left: 194px; top: -32px; background-color: gray !important; color: white; }</code-pane></div><mat-card class="mat-card"><mat-tab-group class="code-tab-group mat-tab-group mat-primary" disableripple=""><mat-tab-header class="mat-tab-header mat-tab-header-pagination-controls-enabled"><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-before mat-elevation-z4 mat-ripple mat-tab-header-pagination-disabled" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div><div class="mat-tab-label-container"><div class="mat-tab-list" role="tablist" style="transform:translateX(0)"><div class="mat-tab-labels"><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple mat-tab-label-active ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-1-0" tabindex="0" aria-posinset="1" aria-setsize="3" aria-controls="mat-tab-content-1-0" aria-selected="true" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">heroes/heroes.component.html</span></div></div><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-1-1" tabindex="-1" aria-posinset="2" aria-setsize="3" aria-controls="mat-tab-content-1-1" aria-selected="false" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">heroes/heroes.component.ts</span></div></div><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-1-2" tabindex="-1" aria-posinset="3" aria-setsize="3" aria-controls="mat-tab-content-1-2" aria-selected="false" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">heroes/heroes.component.css</span></div></div></div><mat-ink-bar class="mat-ink-bar" style="visibility:visible;left:0;width:241px"></mat-ink-bar></div></div><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-after mat-elevation-z4 mat-ripple" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div></mat-tab-header><div class="mat-tab-body-wrapper"><mat-tab-body class="mat-tab-body ng-tns-c12-5 mat-tab-body-active ng-star-inserted" role="tabpanel" id="mat-tab-content-1-0" aria-labelledby="mat-tab-label-1-0"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:none"><aio-code class="ng-star-inserted" style=""><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from heroes/heroes.component.html">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><ol class="linenums"><li class="L0"><span class="tag">&lt;h2&gt;</span><span class="pln">My Heroes</span><span class="tag">&lt;/h2&gt;</span></li><li class="L1"><span class="pln">&nbsp;</span></li><li class="L2"><span class="tag">&lt;div&gt;</span></li><li class="L3"><span class="pln">  </span><span class="tag">&lt;label&gt;</span><span class="pln">Hero name:</span></li><li class="L4"><span class="pln">    </span><span class="tag">&lt;input</span><span class="pln"> #</span><span class="atn">heroName</span><span class="pln"> </span><span class="tag">/&gt;</span></li><li class="L5"><span class="pln">  </span><span class="tag">&lt;/label&gt;</span></li><li class="L6"><span class="pln">  </span><span class="com">&lt;!-- (click) passes input value to add() and then clears the input --&gt;</span></li><li class="L7"><span class="pln">  </span><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"add(heroName.value); heroName.value=''"</span><span class="tag">&gt;</span></li><li class="L8"><span class="pln">    add</span></li><li class="L9"><span class="pln">  </span><span class="tag">&lt;/button&gt;</span></li><li class="L0"><span class="tag">&lt;/div&gt;</span></li><li class="L1"><span class="pln">&nbsp;</span></li><li class="L2"><span class="tag">&lt;ul</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"heroes"</span><span class="tag">&gt;</span></li><li class="L3"><span class="pln">  </span><span class="tag">&lt;li</span><span class="pln"> *</span><a href="api/common/NgForOf" class="code-anchor"><span class="atn">ngFor</span></a><span class="pun">=</span><span class="atv">"let hero of heroes"</span><span class="tag">&gt;</span></li><li class="L4"><span class="pln">    </span><span class="tag">&lt;</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="pln"> </span><a href="api/router/RouterLink" class="code-anchor"><span class="atn">routerLink</span></a><span class="pun">=</span><span class="atv">"/detail/{{hero.id}}"</span><span class="tag">&gt;</span></li><li class="L5"><span class="pln">      </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"badge"</span><span class="tag">&gt;</span><span class="pln">{{hero.id}}</span><span class="tag">&lt;/span&gt;</span><span class="pln"> {{hero.name}}</span></li><li class="L6"><span class="pln">    </span><span class="tag">&lt;/</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="tag">&gt;</span></li><li class="L7"><span class="pln">    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"delete"</span><span class="pln"> </span><span class="atn">title</span><span class="pun">=</span><span class="atv">"delete hero"</span></li><li class="L8"><span class="pln">      (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"delete(hero)"</span><span class="tag">&gt;</span><span class="pln">x</span><span class="tag">&lt;/button&gt;</span></li><li class="L9"><span class="pln">  </span><span class="tag">&lt;/li&gt;</span></li><li class="L0"><span class="tag">&lt;/ul&gt;</span></li></ol></code>
    </pre></aio-code></div></mat-tab-body><mat-tab-body class="mat-tab-body ng-tns-c12-6 ng-star-inserted" role="tabpanel" id="mat-tab-content-1-1" aria-labelledby="mat-tab-label-1-1"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:translate3d(100%,0,0);min-height:1px"></div></mat-tab-body><mat-tab-body class="mat-tab-body ng-tns-c12-7 ng-star-inserted" role="tabpanel" id="mat-tab-content-1-2" aria-labelledby="mat-tab-label-1-2"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:translate3d(100%,0,0);min-height:1px"></div></mat-tab-body></div></mat-tab-group></mat-card></code-tabs><a id="herodetailcomponent"></a><h4 id="herodetailcomponent"><em>HeroDetailComponent</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#herodetailcomponent"><i class="material-icons">link</i></a></h4><code-tabs ng-version="7.0.0"><div style="display:none"><code-pane header="hero-detail/hero-detail.component.html" path="toh-pt6/src/app/hero-detail/hero-detail.component.html">&lt;div *<a href="api/common/NgIf" class="code-anchor">ngIf</a>="hero"&gt; &lt;h2&gt;{{hero.name | <a href="api/common/UpperCasePipe" class="code-anchor">uppercase</a>}} Details&lt;/h2&gt; &lt;div&gt;&lt;span&gt;id: &lt;/span&gt;{{hero.id}}&lt;/div&gt; &lt;div&gt; &lt;label&gt;name: &lt;input [(<a href="api/forms/NgModel" class="code-anchor">ngModel</a>)]="hero.name" placeholder="name"/&gt; &lt;/label&gt; &lt;/div&gt; &lt;button (click)="goBack()"&gt;go back&lt;/button&gt; &lt;button (click)="save()"&gt;save&lt;/button&gt; &lt;/div&gt;</code-pane><code-pane header="hero-detail/hero-detail.component.ts" path="toh-pt6/src/app/hero-detail/hero-detail.component.ts">import { <a href="api/core/Component" class="code-anchor">Component</a>, <a href="api/core/OnInit" class="code-anchor">OnInit</a>, <a href="api/core/Input" class="code-anchor">Input</a> } from '@angular/core'; import { <a href="api/router/ActivatedRoute" class="code-anchor">ActivatedRoute</a> } from '@angular/router'; import { <a href="api/common/Location" class="code-anchor">Location</a> } from '@angular/common'; import { Hero } from '../hero'; import { HeroService } from '../hero.service'; @<a href="api/core/Component" class="code-anchor">Component</a>({ selector: 'app-hero-detail', templateUrl: './hero-detail.component.html', <a href="api/core/Component#styleUrls" class="code-anchor">styleUrls</a>: [ './hero-detail.component.css' ] }) export class HeroDetailComponent implements <a href="api/core/OnInit" class="code-anchor">OnInit</a> { @<a href="api/core/Input" class="code-anchor">Input</a>() hero: Hero; constructor( private route: <a href="api/router/ActivatedRoute" class="code-anchor">ActivatedRoute</a>, private heroService: HeroService, private <a href="api/core/ComponentRef#location" class="code-anchor">location</a>: <a href="api/common/Location" class="code-anchor">Location</a> ) {} ngOnInit(): void { this.getHero(); } getHero(): void { const id = +this.route.snapshot.paramMap.get('id'); this.heroService.getHero(id) .subscribe(hero =&gt; this.hero = hero); } goBack(): void { this.location.back(); } save(): void { this.heroService.updateHero(this.hero) .subscribe(() =&gt; this.goBack()); } }</code-pane></div><mat-card class="mat-card"><mat-tab-group class="code-tab-group mat-tab-group mat-primary" disableripple=""><mat-tab-header class="mat-tab-header"><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-before mat-elevation-z4 mat-ripple mat-tab-header-pagination-disabled" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div><div class="mat-tab-label-container"><div class="mat-tab-list" role="tablist" style="transform:translateX(0)"><div class="mat-tab-labels"><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple mat-tab-label-active ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-2-0" tabindex="0" aria-posinset="1" aria-setsize="2" aria-controls="mat-tab-content-2-0" aria-selected="true" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">hero-detail/hero-detail.component.html</span></div></div><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-2-1" tabindex="-1" aria-posinset="2" aria-setsize="2" aria-controls="mat-tab-content-2-1" aria-selected="false" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">hero-detail/hero-detail.component.ts</span></div></div></div><mat-ink-bar class="mat-ink-bar" style="visibility:visible;left:0;width:288px"></mat-ink-bar></div></div><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-after mat-elevation-z4 mat-ripple mat-tab-header-pagination-disabled" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div></mat-tab-header><div class="mat-tab-body-wrapper"><mat-tab-body class="mat-tab-body ng-tns-c12-8 mat-tab-body-active ng-star-inserted" role="tabpanel" id="mat-tab-content-2-0" aria-labelledby="mat-tab-label-2-0"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:none"><aio-code class="ng-star-inserted" style=""><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from hero-detail/hero-detail.component.html">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="tag">&lt;div</span><span class="pln"> *</span><a href="api/common/NgIf" class="code-anchor"><span class="atn">ngIf</span></a><span class="pun">=</span><span class="atv">"hero"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;h2&gt;</span><span class="pln">{{hero.name | </span><a href="api/common/UpperCasePipe" class="code-anchor"><span class="pln">uppercase</span></a><span class="pln">}} Details</span><span class="tag">&lt;/h2&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;&lt;span&gt;</span><span class="pln">id: </span><span class="tag">&lt;/span&gt;</span><span class="pln">{{hero.id}}</span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label&gt;</span><span class="pln">name:
      </span><span class="tag">&lt;input</span><span class="pln"> [(</span><a href="api/forms/NgModel" class="code-anchor"><span class="atn">ngModel</span></a><span class="pln">)]</span><span class="pun">=</span><span class="atv">"hero.name"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"name"</span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;/label&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"goBack()"</span><span class="tag">&gt;</span><span class="pln">go back</span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"save()"</span><span class="tag">&gt;</span><span class="pln">save</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></code>
    </pre></aio-code></div></mat-tab-body><mat-tab-body class="mat-tab-body ng-tns-c12-9 ng-star-inserted" role="tabpanel" id="mat-tab-content-2-1" aria-labelledby="mat-tab-label-2-1"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:translate3d(100%,0,0);min-height:1px"></div></mat-tab-body></div></mat-tab-group></mat-card></code-tabs><a id="dashboardcomponent"></a><h4 id="dashboardcomponent"><em>DashboardComponent</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#dashboardcomponent"><i class="material-icons">link</i></a></h4><code-tabs ng-version="7.0.0"><div style="display:none"><code-pane header="src/app/dashboard/dashboard.component.html" path="toh-pt6/src/app/dashboard/dashboard.component.html">&lt;h3&gt;Top Heroes&lt;/h3&gt; &lt;div class="grid grid-pad"&gt; &lt;<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> *<a href="api/common/NgForOf" class="code-anchor">ngFor</a>="let hero of heroes" class="col-1-4" <a href="api/router/RouterLink" class="code-anchor">routerLink</a>="/detail/{{hero.id}}"&gt; &lt;div class="module hero"&gt; &lt;h4&gt;{{hero.name}}&lt;/h4&gt; &lt;/div&gt; &lt;/<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a>&gt; &lt;/div&gt; &lt;app-hero-search&gt;&lt;/app-hero-search&gt;</code-pane></div><mat-card class="mat-card"><mat-tab-group class="code-tab-group mat-tab-group mat-primary" disableripple=""><mat-tab-header class="mat-tab-header"><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-before mat-elevation-z4 mat-ripple mat-tab-header-pagination-disabled" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div><div class="mat-tab-label-container"><div class="mat-tab-list" role="tablist" style="transform:translateX(0)"><div class="mat-tab-labels"><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple mat-tab-label-active ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-3-0" tabindex="0" aria-posinset="1" aria-setsize="1" aria-controls="mat-tab-content-3-0" aria-selected="true" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">src/app/dashboard/dashboard.component.html</span></div></div></div><mat-ink-bar class="mat-ink-bar" style="visibility:visible;left:0;width:338px"></mat-ink-bar></div></div><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-after mat-elevation-z4 mat-ripple mat-tab-header-pagination-disabled" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div></mat-tab-header><div class="mat-tab-body-wrapper"><mat-tab-body class="mat-tab-body ng-tns-c12-10 mat-tab-body-active ng-star-inserted" role="tabpanel" id="mat-tab-content-3-0" aria-labelledby="mat-tab-label-3-0"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:none"><aio-code class="ng-star-inserted" style=""><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from src/app/dashboard/dashboard.component.html">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="tag">&lt;h3&gt;</span><span class="pln">Top Heroes</span><span class="tag">&lt;/h3&gt;</span><span class="pln">
</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"grid grid-pad"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="pln"> *</span><a href="api/common/NgForOf" class="code-anchor"><span class="atn">ngFor</span></a><span class="pun">=</span><span class="atv">"let hero of heroes"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"col-1-4"</span><span class="pln">
      </span><a href="api/router/RouterLink" class="code-anchor"><span class="atn">routerLink</span></a><span class="pun">=</span><span class="atv">"/detail/{{hero.id}}"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"module hero"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;h4&gt;</span><span class="pln">{{hero.name}}</span><span class="tag">&lt;/h4&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;/</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;app-hero-search&gt;&lt;/app-hero-search&gt;</span></code>
    </pre></aio-code></div></mat-tab-body></div></mat-tab-group></mat-card></code-tabs><a id="herosearchcomponent"></a><h4 id="herosearchcomponent"><em>HeroSearchComponent</em><a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#herosearchcomponent"><i class="material-icons">link</i></a></h4><code-tabs ng-version="7.0.0"><div style="display:none"><code-pane header="hero-search/hero-search.component.html" path="toh-pt6/src/app/hero-search/hero-search.component.html">&lt;div id="search-component"&gt; &lt;h4&gt;Hero Search&lt;/h4&gt; &lt;input #searchBox id="search-box" (input)="<a href="api/common/PlatformLocation#search" class="code-anchor">search</a>(searchBox.value)" /&gt; &lt;ul class="search-result"&gt; &lt;li *<a href="api/common/NgForOf" class="code-anchor">ngFor</a>="let hero of heroes$ | <a href="api/core/testing/async" class="code-anchor">async</a>" &gt; &lt;<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> <a href="api/router/RouterLink" class="code-anchor">routerLink</a>="/detail/{{hero.id}}"&gt; {{hero.name}} &lt;/<a href="api/router/RouterLinkWithHref" class="code-anchor">a</a>&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/div&gt;</code-pane><code-pane header="hero-search/hero-search.component.ts" path="toh-pt6/src/app/hero-search/hero-search.component.ts">import { <a href="api/core/Component" class="code-anchor">Component</a>, <a href="api/core/OnInit" class="code-anchor">OnInit</a> } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; import { Hero } from '../hero'; import { HeroService } from '../hero.service'; @<a href="api/core/Component" class="code-anchor">Component</a>({ selector: 'app-hero-search', templateUrl: './hero-search.component.html', <a href="api/core/Component#styleUrls" class="code-anchor">styleUrls</a>: [ './hero-search.component.css' ] }) export class HeroSearchComponent implements <a href="api/core/OnInit" class="code-anchor">OnInit</a> { heroes$: Observable&lt;Hero[]&gt;; private searchTerms = new Subject&lt;string&gt;(); constructor(private heroService: HeroService) {} // Push <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> term into the observable stream. <a href="api/common/PlatformLocation#search" class="code-anchor">search</a>(term: string): void { this.searchTerms.next(term); } ngOnInit(): void { this.heroes$ = this.searchTerms.pipe( // wait 300ms after each keystroke before considering the term debounceTime(300), // ignore new term if same as <a href="api/service-worker/UpdateActivatedEvent#previous" class="code-anchor">previous</a> term distinctUntilChanged(), // switch to new <a href="api/common/PlatformLocation#search" class="code-anchor">search</a> observable each time the term <a href="api/core/QueryList#changes" class="code-anchor">changes</a> switchMap((term: string) =&gt; this.heroService.searchHeroes(term)), ); } }</code-pane><code-pane header="hero-search/hero-search.component.css" path="toh-pt6/src/app/hero-search/hero-search.component.css">/* HeroSearch private styles */ .search-result li { border-bottom: 1px solid gray; border-left: 1px solid gray; border-right: 1px solid gray; width: 195px; height: 16px; padding: 5px; background-color: white; cursor: pointer; list-style-type: none; } .search-result li:hover { background-color: #607D8B; } .search-result li <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a> { color: #888; display: block; text-decoration: none; } .search-result li <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a>:hover { color: white; } .search-result li <a href="api/router/RouterLinkWithHref" class="code-anchor">a</a>:active { color: white; } #search-box { width: 200px; height: 20px; } ul.search-result { margin-top: 0; padding-left: 0; }</code-pane></div><mat-card class="mat-card"><mat-tab-group class="code-tab-group mat-tab-group mat-primary" disableripple=""><mat-tab-header class="mat-tab-header mat-tab-header-pagination-controls-enabled"><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-before mat-elevation-z4 mat-ripple mat-tab-header-pagination-disabled" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div><div class="mat-tab-label-container"><div class="mat-tab-list" role="tablist" style="transform:translateX(0)"><div class="mat-tab-labels"><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple mat-tab-label-active ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-4-0" tabindex="0" aria-posinset="1" aria-setsize="3" aria-controls="mat-tab-content-4-0" aria-selected="true" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">hero-search/hero-search.component.html</span></div></div><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-4-1" tabindex="-1" aria-posinset="2" aria-setsize="3" aria-controls="mat-tab-content-4-1" aria-selected="false" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">hero-search/hero-search.component.ts</span></div></div><div cdkmonitorelementfocus="" class="mat-tab-label mat-ripple ng-star-inserted" mat-ripple="" mattablabelwrapper="" role="tab" id="mat-tab-label-4-2" tabindex="-1" aria-posinset="3" aria-setsize="3" aria-controls="mat-tab-content-4-2" aria-selected="false" aria-disabled="false"><div class="mat-tab-label-content"><span class="ng-star-inserted">hero-search/hero-search.component.css</span></div></div></div><mat-ink-bar class="mat-ink-bar" style="visibility:visible;left:0;width:305px"></mat-ink-bar></div></div><div aria-hidden="true" class="mat-tab-header-pagination mat-tab-header-pagination-after mat-elevation-z4 mat-ripple" mat-ripple=""><div class="mat-tab-header-pagination-chevron"></div></div></mat-tab-header><div class="mat-tab-body-wrapper"><mat-tab-body class="mat-tab-body ng-tns-c12-11 mat-tab-body-active ng-star-inserted" role="tabpanel" id="mat-tab-content-4-0" aria-labelledby="mat-tab-label-4-0"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:none"><aio-code class="ng-star-inserted" style=""><pre class="prettyprint lang-">      <button class="material-icons copy-button no-print ng-star-inserted" title="Copy code snippet" aria-label="Copy code snippet from hero-search/hero-search.component.html">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><ol class="linenums"><li class="L0"><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"search-component"</span><span class="tag">&gt;</span></li><li class="L1"><span class="pln">  </span><span class="tag">&lt;h4&gt;</span><span class="pln">Hero Search</span><span class="tag">&lt;/h4&gt;</span></li><li class="L2"><span class="pln">&nbsp;</span></li><li class="L3"><span class="pln">  </span><span class="tag">&lt;input</span><span class="pln"> #</span><span class="atn">searchBox</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"search-box"</span><span class="pln"> (</span><span class="atn">input</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"</span><a href="api/common/PlatformLocation#search" class="code-anchor"><span class="atv">search</span></a><span class="atv">(searchBox.value)"</span><span class="pln"> </span><span class="tag">/&gt;</span></li><li class="L4"><span class="pln">&nbsp;</span></li><li class="L5"><span class="pln">  </span><span class="tag">&lt;ul</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"search-result"</span><span class="tag">&gt;</span></li><li class="L6"><span class="pln">    </span><span class="tag">&lt;li</span><span class="pln"> *</span><a href="api/common/NgForOf" class="code-anchor"><span class="atn">ngFor</span></a><span class="pun">=</span><span class="atv">"let hero of heroes$ | </span><a href="api/core/testing/async" class="code-anchor"><span class="atv">async</span></a><span class="atv">"</span><span class="pln"> </span><span class="tag">&gt;</span></li><li class="L7"><span class="pln">      </span><span class="tag">&lt;</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="pln"> </span><a href="api/router/RouterLink" class="code-anchor"><span class="atn">routerLink</span></a><span class="pun">=</span><span class="atv">"/detail/{{hero.id}}"</span><span class="tag">&gt;</span></li><li class="L8"><span class="pln">        {{hero.name}}</span></li><li class="L9"><span class="pln">      </span><span class="tag">&lt;/</span><a href="api/router/RouterLinkWithHref" class="code-anchor"><span class="tag">a</span></a><span class="tag">&gt;</span></li><li class="L0"><span class="pln">    </span><span class="tag">&lt;/li&gt;</span></li><li class="L1"><span class="pln">  </span><span class="tag">&lt;/ul&gt;</span></li><li class="L2"><span class="tag">&lt;/div&gt;</span></li></ol></code>
    </pre></aio-code></div></mat-tab-body><mat-tab-body class="mat-tab-body ng-tns-c12-12 ng-star-inserted" role="tabpanel" id="mat-tab-content-4-1" aria-labelledby="mat-tab-label-4-1"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:translate3d(100%,0,0);min-height:1px"></div></mat-tab-body><mat-tab-body class="mat-tab-body ng-tns-c12-13 ng-star-inserted" role="tabpanel" id="mat-tab-content-4-2" aria-labelledby="mat-tab-label-4-2"><div class="mat-tab-body-content ng-trigger ng-trigger-translateTab" style="transform:translate3d(100%,0,0);min-height:1px"></div></mat-tab-body></div></mat-tab-group></mat-card></code-tabs><h2 id="summary" translation-result="on">小结<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#summary"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="summary">Summary<a title="Link to this heading" class="header-link" aria-hidden="true" href="tutorial/toh-pt6#summary"><i class="material-icons">link</i></a></h2><p translation-result="on">旅程即将结束，不过你已经收获颇丰。</p><p translation-origin="off">You're at the end of your journey, and you've accomplished a lot.</p><ul><li><p translation-result="on">你添加了在应用程序中使用 HTTP 的必备依赖。</p><p translation-origin="off">You added the necessary dependencies to use HTTP in the app.</p></li><li><p translation-result="on">你重构了 <code>HeroService</code>，以通过 web API 来加载英雄数据。</p><p translation-origin="off">You refactored <code>HeroService</code> to load heroes from a web API.</p></li><li><p translation-result="on">你扩展了 <code>HeroService</code> 来支持 <code>post()</code>、<code>put()</code> 和 <code>delete()</code> 方法。</p><p translation-origin="off">You extended <code>HeroService</code> to support <code>post()</code>, <code>put()</code>, and <code>delete()</code> methods.</p></li><li><p translation-result="on">你修改了组件，以允许用户添加、编辑和删除英雄。</p><p translation-origin="off">You updated the components to allow adding, editing, and deleting of heroes.</p></li><li><p translation-result="on">你配置了一个内存 Web API。</p><p translation-origin="off">You configured an in-memory web API.</p></li><li><p translation-result="on">你学会了如何使用“可观察对象”。</p><p translation-origin="off">You learned how to use observables.</p></li></ul><p translation-result="on">《英雄指南》教程结束了。 如果你准备开始学习 Angular 开发的原理，请开始 <a href="guide/architecture" title="Architecture">架构</a> 一章。</p><p translation-origin="off">This concludes the "Tour of Heroes" tutorial. You're ready to learn more about Angular development in the fundamentals section, starting with the <a href="guide/architecture" title="Architecture">Architecture</a> guide.</p></div></div></aio-doc-viewer></main></mat-sidenav-content></mat-sidenav-container><div class="toc-container no-print ng-star-inserted"><aio-lazy-ce selector="aio-toc"><aio-toc ng-version="7.0.0"><div class="toc-inner no-print collapsed ng-star-inserted"><ul class="toc-list"><li title="HTTPlink" class="h1 ng-star-inserted active"><a href="tutorial/toh-pt6#http">HTTP</a></li><li title="启用 HTTP 服务link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#enable-http-services">启用 HTTP 服务</a></li><li title="模拟数据服务器link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#simulate-a-data-server">模拟数据服务器</a></li><li title="英雄与 HTTPlink" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#heroes-and-http">英雄与 HTTP</a></li><li title="通过 HttpClient 获取英雄link" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#get-heroes-with-httpclient">通过 <code>HttpClient</code> 获取英雄</a></li><li title="Http 方法返回单个值link" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#http-methods-return-one-value">Http 方法返回单个值</a></li><li title="HttpClient.get 返回响应数据link" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#httpclientget-returns-response-data"><code>HttpClient.get</code> 返回响应数据</a></li><li title="错误处理link" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#error-handling">错误处理</a></li><li title="窥探 Observablelink" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#tap-into-the-observable">窥探 <code>Observable</code></a></li><li title="通过 id 获取英雄link" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#get-hero-by-id">通过 id 获取英雄</a></li><li title="修改英雄link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#update-heroes">修改英雄</a></li><li title="添加新英雄link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#add-a-new-hero">添加新英雄</a></li><li title="删除某个英雄link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#delete-a-hero">删除某个英雄</a></li><li title="根据名字搜索link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#search-by-name">根据名字搜索</a></li><li title="为仪表盘添加搜索功能link" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#add-search-to-the-dashboard">为仪表盘添加搜索功能</a></li><li title="创建 HeroSearchComponentlink" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#create-herosearchcomponent">创建 <code>HeroSearchComponent</code></a></li><li title="AsyncPipelink" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#asyncpipe"><em>AsyncPipe</em></a></li><li title="修正 HeroSearchComponent 类link" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#fix-the-herosearchcomponent-class">修正 <code>HeroSearchComponent</code> 类</a></li><li title="RxJS Subject 类型的 searchTermslink" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#the-searchterms-rxjs-subject">RxJS <code>Subject</code> 类型的 <code>searchTerms</code></a></li><li title="串联 RxJS 操作符link" class="h3 ng-star-inserted"><a href="tutorial/toh-pt6#chaining-rxjs-operators">串联 RxJS 操作符</a></li><li title="查看最终代码link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#final-code-review">查看最终代码</a></li><li title="小结link" class="h2 ng-star-inserted"><a href="tutorial/toh-pt6#summary">小结</a></li></ul></div></aio-toc></aio-lazy-ce></div><footer class="no-print"><aio-footer><div class="grid-fluid"><div class="footer-block ng-star-inserted"><h3>资源</h3><ul><li class="ng-star-inserted"><a class="link" href="about" title="Angular 贡献者。">关于</a></li><li class="ng-star-inserted"><a class="link" href="resources" title="网络上的 Angular 工具、培训、博客等">资源列表</a></li><li class="ng-star-inserted"><a class="link" href="presskit" title="我们的联系方式、LOGO 和品牌">宣传资料</a></li><li class="ng-star-inserted"><a class="link" href="https://blog.angular.io/" title="Angular 官方博客">博客</a></li></ul></div><div class="footer-block ng-star-inserted"><h3>帮助</h3><ul><li class="ng-star-inserted"><a class="link" href="https://stackoverflow.com/questions/tagged/angular" title="Stack Overflow: 这里的社区会回答你关于 Angular 的技术问题">Stack Overflow</a></li><li class="ng-star-inserted"><a class="link" href="https://gitter.im/angular/angular" title="和老鸟聊 Angular">Gitter</a></li><li class="ng-star-inserted"><a class="link" href="https://github.com/angular/angular/issues" title="在 github 上报告问题和建议。">报告问题</a></li><li class="ng-star-inserted"><a class="link" href="https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md" title="让我们彼此尊重">行为规范</a></li></ul></div><div class="footer-block ng-star-inserted"><h3>社区</h3><ul><li class="ng-star-inserted"><a class="link" href="events" title="Post issues and suggestions on github.">活动</a></li><li class="ng-star-inserted"><a class="link" href="http://www.meetup.com/topics/angularjs/" title="参加聚会，向别的开发人员学习">聚会</a></li><li class="ng-star-inserted"><a class="link" href="https://twitter.com/angular" title="Twitter">Twitter</a></li><li class="ng-star-inserted"><a class="link" href="https://github.com/angular/angular" title="GitHub">GitHub</a></li><li class="ng-star-inserted"><a class="link" href="contribute" title="向 Angular 做贡献">做贡献</a></li></ul></div><div class="footer-block ng-star-inserted"><h3>多语言</h3><ul><li class="ng-star-inserted"><a class="link" href="https://angular.io/" title="English Version.">English Version</a></li><li class="ng-star-inserted"><a class="link" href="https://angular.jp/" title="日本語版">日本語版</a></li><li class="ng-star-inserted"><a class="link" href="https://angular.kr/" title="한국어">한국어</a></li></ul></div></div><p>Super-powered by Google ©2010-2019. 代码授权方式：<a href="license" title="License text">MIT-style License</a>. 文档授权方式：<a href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.</p><p>当前版本：8.0.0-build.95+sha.43aadf1.</p></aio-footer></footer><mat-icon class="cdk-visually-hidden mat-icon material-icons ng-star-inserted" role="img" aria-hidden="true">&nbsp;</mat-icon></aio-shell><noscript><div class="background-sky hero"></div><section id="intro" style="text-shadow:1px 1px #1976d2"><div class="hero-logo"><img src="assets/images/logos/angular/angular.svg" width="250" height="250" alt="Angular"></div><div class="homepage-container"><div class="hero-headline">一套框架，多种平台<br>移动 &amp; 桌面</div></div></section><h2 style="color:red;margin-top:40px;position:relative;text-align:center;text-shadow:1px 1px #fafafa"><b><i>该网站需要浏览器支持 JavaScript</i></b></h2></noscript><script src="runtime.fa3355727d5250409e08.js"></script><script src="polyfills.a2efc1c1a62312ff1f80.js"></script><script src="main.02884fe4cde5ede2ec4e.js"></script><div class="cdk-live-announcer-element cdk-visually-hidden" aria-atomic="true" aria-live="polite"></div></body></html>